Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui into feature/unified_grid

This commit is contained in:
Pfeil, Scott Robert 2021-02-22 15:11:51 -05:00
commit ca8884a477
126 changed files with 2178 additions and 788 deletions

View File

@ -62,6 +62,8 @@
01EB369323609801006832FA /* HeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368C23609801006832FA /* HeaderModel.swift */; }; 01EB369323609801006832FA /* HeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368C23609801006832FA /* HeaderModel.swift */; };
01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368D23609801006832FA /* HeadlineBodyModel.swift */; }; 01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368D23609801006832FA /* HeadlineBodyModel.swift */; };
01F2A03223A4498200D954D8 /* CaretLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F2A03123A4498200D954D8 /* CaretLinkModel.swift */; }; 01F2A03223A4498200D954D8 /* CaretLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F2A03123A4498200D954D8 /* CaretLinkModel.swift */; };
0A0FEC7425D42A5E00AF2548 /* BaseItemPickerEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0FEC7325D42A5E00AF2548 /* BaseItemPickerEntryField.swift */; };
0A0FEC7825D42A8500AF2548 /* BaseItemPickerEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0FEC7725D42A8500AF2548 /* BaseItemPickerEntryFieldModel.swift */; };
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; };
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */; }; 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */; };
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB7E235DECC500C160A2 /* EntryField.swift */; }; 0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB7E235DECC500C160A2 /* EntryField.swift */; };
@ -94,7 +96,7 @@
0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */; }; 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */; };
0A7EF85F23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */; }; 0A7EF85F23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */; };
0A7EF86123D8AC2500B2AAD1 /* DigitEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86023D8AC2500B2AAD1 /* DigitEntryFieldModel.swift */; }; 0A7EF86123D8AC2500B2AAD1 /* DigitEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86023D8AC2500B2AAD1 /* DigitEntryFieldModel.swift */; };
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86223D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift */; }; 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86223D8AFA000B2AAD1 /* BaseDropdownFieldModel.swift */; };
0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */; }; 0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */; };
0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */; }; 0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */; };
0A849EFE246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A849EFD246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift */; }; 0A849EFE246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A849EFD246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift */; };
@ -105,6 +107,7 @@
0A9D09212433796500D2E6C0 /* CarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091B2433796500D2E6C0 /* CarouselIndicatorModel.swift */; }; 0A9D09212433796500D2E6C0 /* CarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091B2433796500D2E6C0 /* CarouselIndicatorModel.swift */; };
0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */; }; 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */; };
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; }; 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; };
0AA4D2E125CAEC72008DB32D /* AccessibilityModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */; };
0AB000BA24BF63490090C5E7 /* ModalListPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */; }; 0AB000BA24BF63490090C5E7 /* ModalListPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */; };
0AB000BC24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */; }; 0AB000BC24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */; };
0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */; }; 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */; };
@ -113,6 +116,8 @@
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */; }; 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */; };
0AD93A9F24C0AA5100E56A97 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7918F423F5E7EA00772FF4 /* ImageView.swift */; }; 0AD93A9F24C0AA5100E56A97 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7918F423F5E7EA00772FF4 /* ImageView.swift */; };
0AE14F64238315D2005417F8 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14F63238315D2005417F8 /* TextField.swift */; }; 0AE14F64238315D2005417F8 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14F63238315D2005417F8 /* TextField.swift */; };
0AE277E925D2ED4B0048A38D /* MultiItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE277E825D2ED4B0048A38D /* MultiItemDropdownEntryField.swift */; };
0AE277EC25D2EE310048A38D /* MultiItemDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE277EB25D2EE310048A38D /* MultiItemDropdownEntryFieldModel.swift */; };
0AE98BAF23FEF956004C5109 /* ExternalLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BAE23FEF956004C5109 /* ExternalLink.swift */; }; 0AE98BAF23FEF956004C5109 /* ExternalLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BAE23FEF956004C5109 /* ExternalLink.swift */; };
0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */; }; 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */; };
0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB423FF18D2004C5109 /* Arrow.swift */; }; 0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB423FF18D2004C5109 /* Arrow.swift */; };
@ -290,6 +295,7 @@
BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBC0C4FE24811DCA0087C44F /* TagModel.swift */; }; BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBC0C4FE24811DCA0087C44F /* TagModel.swift */; };
C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; }; C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; };
C07065C42395677300FBF997 /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07065C32395677300FBF997 /* Link.swift */; }; C07065C42395677300FBF997 /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07065C32395677300FBF997 /* Link.swift */; };
C6687441259D92D400F32D13 /* ActionTopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6687440259D92D400F32D13 /* ActionTopNotificationModel.swift */; };
C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */; }; C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */; };
C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A68023C9830D00BFB94E /* NumberedListModel.swift */; }; C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A68023C9830D00BFB94E /* NumberedListModel.swift */; };
C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; }; C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; };
@ -345,6 +351,8 @@
D23EA800247EBD6C00D60C34 /* LabelBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */; }; D23EA800247EBD6C00D60C34 /* LabelBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */; };
D23EA802247EBED400D60C34 /* ImageBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */; }; D23EA802247EBED400D60C34 /* ImageBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */; };
D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; };
D24918F625D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D24918F525D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift */; };
D24918FA25D5ADBB00CAB4B1 /* PageScrolledClosureBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D24918F925D5ADBA00CAB4B1 /* PageScrolledClosureBehavior.swift */; };
D2509ED12472ED9B001BFB9D /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */; }; D2509ED12472ED9B001BFB9D /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */; };
D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */; }; D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */; };
D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; }; D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; };
@ -401,6 +409,11 @@
D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA74C248589C800B75CB8 /* TabPageModelProtocol.swift */; }; D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA74C248589C800B75CB8 /* TabPageModelProtocol.swift */; };
D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */; }; D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */; };
D29C558A25C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C558925C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift */; };
D29C558D25C05C990082E7D6 /* BGVideoImageMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C558C25C05C990082E7D6 /* BGVideoImageMolecule.swift */; };
D29C559025C095210082E7D6 /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C558F25C095210082E7D6 /* Video.swift */; };
D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559225C0992D0082E7D6 /* VideoModel.swift */; };
D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559525C099630082E7D6 /* VideoDataManager.swift */; };
D29C94D5242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */; }; D29C94D5242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */; };
D29DF0D121E404D4003B2FB9 /* MVMCoreUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF0D121E404D4003B2FB9 /* MVMCoreUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */; }; D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */; };
@ -603,6 +616,8 @@
01EB368C23609801006832FA /* HeaderModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderModel.swift; sourceTree = "<group>"; }; 01EB368C23609801006832FA /* HeaderModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderModel.swift; sourceTree = "<group>"; };
01EB368D23609801006832FA /* HeadlineBodyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyModel.swift; sourceTree = "<group>"; }; 01EB368D23609801006832FA /* HeadlineBodyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyModel.swift; sourceTree = "<group>"; };
01F2A03123A4498200D954D8 /* CaretLinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaretLinkModel.swift; sourceTree = "<group>"; }; 01F2A03123A4498200D954D8 /* CaretLinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaretLinkModel.swift; sourceTree = "<group>"; };
0A0FEC7325D42A5E00AF2548 /* BaseItemPickerEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseItemPickerEntryField.swift; sourceTree = "<group>"; };
0A0FEC7725D42A8500AF2548 /* BaseItemPickerEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseItemPickerEntryFieldModel.swift; sourceTree = "<group>"; };
0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = "<group>"; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = "<group>"; };
0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackViewAlignment+Extension.swift"; sourceTree = "<group>"; }; 0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackViewAlignment+Extension.swift"; sourceTree = "<group>"; };
0A21DB7E235DECC500C160A2 /* EntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = "<group>"; }; 0A21DB7E235DECC500C160A2 /* EntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = "<group>"; };
@ -636,7 +651,7 @@
0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = "<group>"; }; 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = "<group>"; };
0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnEntryFieldModel.swift; sourceTree = "<group>"; }; 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnEntryFieldModel.swift; sourceTree = "<group>"; };
0A7EF86023D8AC2500B2AAD1 /* DigitEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitEntryFieldModel.swift; sourceTree = "<group>"; }; 0A7EF86023D8AC2500B2AAD1 /* DigitEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitEntryFieldModel.swift; sourceTree = "<group>"; };
0A7EF86223D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDropdownEntryFieldModel.swift; sourceTree = "<group>"; }; 0A7EF86223D8AFA000B2AAD1 /* BaseDropdownFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDropdownFieldModel.swift; sourceTree = "<group>"; };
0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryFieldModel.swift; sourceTree = "<group>"; }; 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryFieldModel.swift; sourceTree = "<group>"; };
0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryFieldModel.swift; sourceTree = "<group>"; }; 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryFieldModel.swift; sourceTree = "<group>"; };
0A8321AE2355FE9500CB7F00 /* DigitBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitBox.swift; sourceTree = "<group>"; }; 0A8321AE2355FE9500CB7F00 /* DigitBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitBox.swift; sourceTree = "<group>"; };
@ -649,6 +664,7 @@
0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarouselIndicator.swift; sourceTree = "<group>"; }; 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarouselIndicator.swift; sourceTree = "<group>"; };
0AA33B33239813C50067DD0F /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = "<group>"; }; 0AA33B33239813C50067DD0F /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = "<group>"; };
0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = "<group>"; }; 0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = "<group>"; };
0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityModelProtocol.swift; sourceTree = "<group>"; };
0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalListPageTemplateModel.swift; sourceTree = "<group>"; }; 0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalListPageTemplateModel.swift; sourceTree = "<group>"; };
0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalStackPageTemplateModel.swift; sourceTree = "<group>"; }; 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalStackPageTemplateModel.swift; sourceTree = "<group>"; };
0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Extension.swift"; sourceTree = "<group>"; }; 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Extension.swift"; sourceTree = "<group>"; };
@ -656,6 +672,8 @@
0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryField.swift; sourceTree = "<group>"; }; 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryField.swift; sourceTree = "<group>"; };
0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryField.swift; sourceTree = "<group>"; }; 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryField.swift; sourceTree = "<group>"; };
0AE14F63238315D2005417F8 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = "<group>"; }; 0AE14F63238315D2005417F8 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = "<group>"; };
0AE277E825D2ED4B0048A38D /* MultiItemDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiItemDropdownEntryField.swift; sourceTree = "<group>"; };
0AE277EB25D2EE310048A38D /* MultiItemDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiItemDropdownEntryFieldModel.swift; sourceTree = "<group>"; };
0AE98BAE23FEF956004C5109 /* ExternalLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLink.swift; sourceTree = "<group>"; }; 0AE98BAE23FEF956004C5109 /* ExternalLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLink.swift; sourceTree = "<group>"; };
0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLinkModel.swift; sourceTree = "<group>"; }; 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLinkModel.swift; sourceTree = "<group>"; };
0AE98BB423FF18D2004C5109 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = "<group>"; }; 0AE98BB423FF18D2004C5109 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = "<group>"; };
@ -833,6 +851,7 @@
BBC0C4FE24811DCA0087C44F /* TagModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagModel.swift; sourceTree = "<group>"; }; BBC0C4FE24811DCA0087C44F /* TagModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagModel.swift; sourceTree = "<group>"; };
C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; }; C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
C07065C32395677300FBF997 /* Link.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = "<group>"; }; C07065C32395677300FBF997 /* Link.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = "<group>"; };
C6687440259D92D400F32D13 /* ActionTopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTopNotificationModel.swift; sourceTree = "<group>"; };
C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnOrderedListModel.swift; sourceTree = "<group>"; }; C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnOrderedListModel.swift; sourceTree = "<group>"; };
C695A68023C9830D00BFB94E /* NumberedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberedListModel.swift; sourceTree = "<group>"; }; C695A68023C9830D00BFB94E /* NumberedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberedListModel.swift; sourceTree = "<group>"; };
C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = "<group>"; }; C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = "<group>"; };
@ -888,6 +907,8 @@
D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelBarButtonItem.swift; sourceTree = "<group>"; }; D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelBarButtonItem.swift; sourceTree = "<group>"; };
D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBarButtonItem.swift; sourceTree = "<group>"; }; D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBarButtonItem.swift; sourceTree = "<group>"; };
D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = "<group>"; }; D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = "<group>"; };
D24918F525D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageVisibilityClosureBehavior.swift; sourceTree = "<group>"; };
D24918F925D5ADBA00CAB4B1 /* PageScrolledClosureBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageScrolledClosureBehavior.swift; sourceTree = "<group>"; };
D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = "<group>"; }; D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = "<group>"; };
D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationImageButtonModel.swift; sourceTree = "<group>"; }; D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationImageButtonModel.swift; sourceTree = "<group>"; };
D253BB9B245874F8002DE544 /* BGImageMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageMolecule.swift; sourceTree = "<group>"; }; D253BB9B245874F8002DE544 /* BGImageMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageMolecule.swift; sourceTree = "<group>"; };
@ -942,6 +963,11 @@
D28BA74C248589C800B75CB8 /* TabPageModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPageModelProtocol.swift; sourceTree = "<group>"; }; D28BA74C248589C800B75CB8 /* TabPageModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPageModelProtocol.swift; sourceTree = "<group>"; };
D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewConstrainingProtocol.h; sourceTree = "<group>"; }; D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewConstrainingProtocol.h; sourceTree = "<group>"; };
D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleMolecule.swift; sourceTree = "<group>"; }; D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleMolecule.swift; sourceTree = "<group>"; };
D29C558925C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGVideoImageMoleculeModel.swift; sourceTree = "<group>"; };
D29C558C25C05C990082E7D6 /* BGVideoImageMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGVideoImageMolecule.swift; sourceTree = "<group>"; };
D29C558F25C095210082E7D6 /* Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Video.swift; sourceTree = "<group>"; };
D29C559225C0992D0082E7D6 /* VideoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoModel.swift; sourceTree = "<group>"; };
D29C559525C099630082E7D6 /* VideoDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDataManager.swift; sourceTree = "<group>"; };
D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUICommonViewsUtility+Extension.swift"; sourceTree = "<group>"; }; D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUICommonViewsUtility+Extension.swift"; sourceTree = "<group>"; };
D29DF0CC21E404D4003B2FB9 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D29DF0CC21E404D4003B2FB9 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUI.h; sourceTree = "<group>"; }; D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUI.h; sourceTree = "<group>"; };
@ -1107,6 +1133,7 @@
011B58EE23A2AA850085F53C /* ModelProtocols */ = { 011B58EE23A2AA850085F53C /* ModelProtocols */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */,
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */, D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */,
014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */, 014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */,
D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */, D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */,
@ -1164,6 +1191,39 @@
path = FormUIHelpers; path = FormUIHelpers;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
0A0FEC7125D4246000AF2548 /* Dropdown Fields */ = {
isa = PBXGroup;
children = (
0A7EF86223D8AFA000B2AAD1 /* BaseDropdownFieldModel.swift */,
0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */,
0A0FEC8D25D4487F00AF2548 /* Date Dropdown */,
0A0FEC8A25D4486F00AF2548 /* Item Dropdown */,
);
path = "Dropdown Fields";
sourceTree = "<group>";
};
0A0FEC8A25D4486F00AF2548 /* Item Dropdown */ = {
isa = PBXGroup;
children = (
0A0FEC7725D42A8500AF2548 /* BaseItemPickerEntryFieldModel.swift */,
0A0FEC7325D42A5E00AF2548 /* BaseItemPickerEntryField.swift */,
0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */,
0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */,
0AE277EB25D2EE310048A38D /* MultiItemDropdownEntryFieldModel.swift */,
0AE277E825D2ED4B0048A38D /* MultiItemDropdownEntryField.swift */,
);
path = "Item Dropdown";
sourceTree = "<group>";
};
0A0FEC8D25D4487F00AF2548 /* Date Dropdown */ = {
isa = PBXGroup;
children = (
0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */,
0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */,
);
path = "Date Dropdown";
sourceTree = "<group>";
};
0A5D59C323AD488600EFD9E9 /* Protocols */ = { 0A5D59C323AD488600EFD9E9 /* Protocols */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1223,6 +1283,8 @@
children = ( children = (
27F973522466074500CAB5C5 /* PageBehavior.swift */, 27F973522466074500CAB5C5 /* PageBehavior.swift */,
27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */, 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */,
D24918F525D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift */,
D24918F925D5ADBA00CAB4B1 /* PageScrolledClosureBehavior.swift */,
); );
path = Behaviors; path = Behaviors;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1308,6 +1370,7 @@
D2ED27E9254B0CE600A1C293 /* ActionAlertModel.swift */, D2ED27E9254B0CE600A1C293 /* ActionAlertModel.swift */,
D2ED27EA254B0CE700A1C293 /* AlertModel.swift */, D2ED27EA254B0CE700A1C293 /* AlertModel.swift */,
D2ED27E8254B0CE600A1C293 /* ActionPopupModel.swift */, D2ED27E8254B0CE600A1C293 /* ActionPopupModel.swift */,
C6687440259D92D400F32D13 /* ActionTopNotificationModel.swift */,
); );
path = Actions; path = Actions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1673,6 +1736,8 @@
D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */,
D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */, D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */,
D253BB9B245874F8002DE544 /* BGImageMolecule.swift */, D253BB9B245874F8002DE544 /* BGImageMolecule.swift */,
D29C558925C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift */,
D29C558C25C05C990082E7D6 /* BGVideoImageMolecule.swift */,
); );
path = OtherContainers; path = OtherContainers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1707,6 +1772,8 @@
D2092348244A51D40044AD09 /* RadioSwatchModel.swift */, D2092348244A51D40044AD09 /* RadioSwatchModel.swift */,
AAB9C109243496DD00151545 /* RadioSwatch.swift */, AAB9C109243496DD00151545 /* RadioSwatch.swift */,
AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */, AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */,
D260105223CEA61600764D80 /* ToggleModel.swift */,
0AA33B392398524F0067DD0F /* Toggle.swift */,
AAA7CD68250641F90045B959 /* HeartModel.swift */, AAA7CD68250641F90045B959 /* HeartModel.swift */,
AAA7CD6A250642080045B959 /* Heart.swift */, AAA7CD6A250642080045B959 /* Heart.swift */,
); );
@ -2004,8 +2071,6 @@
D28A838223CCBD3F00DFE4FC /* WheelModel.swift */, D28A838223CCBD3F00DFE4FC /* WheelModel.swift */,
943784F3236B77BB006A1E82 /* Wheel.swift */, 943784F3236B77BB006A1E82 /* Wheel.swift */,
943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */, 943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */,
D260105223CEA61600764D80 /* ToggleModel.swift */,
0AA33B392398524F0067DD0F /* Toggle.swift */,
0AE98BB623FF18E9004C5109 /* ArrowModel.swift */, 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */,
0AE98BB423FF18D2004C5109 /* Arrow.swift */, 0AE98BB423FF18D2004C5109 /* Arrow.swift */,
94382085243238D100B43AF3 /* WebViewModel.swift */, 94382085243238D100B43AF3 /* WebViewModel.swift */,
@ -2017,6 +2082,9 @@
AA37CBD42519072F0027344C /* Stars.swift */, AA37CBD42519072F0027344C /* Stars.swift */,
AA07EA902510A442009A2AE3 /* StarModel.swift */, AA07EA902510A442009A2AE3 /* StarModel.swift */,
AA07EA922510A451009A2AE3 /* Star.swift */, AA07EA922510A451009A2AE3 /* Star.swift */,
D29C559525C099630082E7D6 /* VideoDataManager.swift */,
D29C559225C0992D0082E7D6 /* VideoModel.swift */,
D29C558F25C095210082E7D6 /* Video.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2024,23 +2092,18 @@
D29DF22B21E6A0FA003B2FB9 /* TextFields */ = { D29DF22B21E6A0FA003B2FB9 /* TextFields */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */,
0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */,
0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */, 0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */,
0A21DB7E235DECC500C160A2 /* EntryField.swift */, 0A21DB7E235DECC500C160A2 /* EntryField.swift */,
0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */,
0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */,
0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */, 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */,
0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */, 0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */,
0A8321AE2355FE9500CB7F00 /* DigitBox.swift */, 0A8321AE2355FE9500CB7F00 /* DigitBox.swift */,
0A7EF86023D8AC2500B2AAD1 /* DigitEntryFieldModel.swift */, 0A7EF86023D8AC2500B2AAD1 /* DigitEntryFieldModel.swift */,
0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */, 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */,
0A7EF86223D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift */,
0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */,
0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */,
0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */,
0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */,
0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */,
0A25209724645B76000FA9F6 /* TextViewEntryFieldModel.swift */, 0A25209724645B76000FA9F6 /* TextViewEntryFieldModel.swift */,
0A25209524645AFD000FA9F6 /* TextViewEntryField.swift */, 0A25209524645AFD000FA9F6 /* TextViewEntryField.swift */,
0A0FEC7125D4246000AF2548 /* Dropdown Fields */,
); );
path = TextFields; path = TextFields;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2154,6 +2217,7 @@
D2B18B7D236090D500A9AEDC /* BaseClasses */ = { D2B18B7D236090D500A9AEDC /* BaseClasses */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0A5D59C323AD488600EFD9E9 /* Protocols */,
0A6682B3243769C700AD3CA1 /* TextView.swift */, 0A6682B3243769C700AD3CA1 /* TextView.swift */,
C003506023AA94CD00B6AC29 /* Button.swift */, C003506023AA94CD00B6AC29 /* Button.swift */,
D2B18B7E2360913400A9AEDC /* Control.swift */, D2B18B7E2360913400A9AEDC /* Control.swift */,
@ -2164,7 +2228,6 @@
BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */, BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */,
D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */, D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */,
D264FAA92440F97600D98315 /* CollectionView.swift */, D264FAA92440F97600D98315 /* CollectionView.swift */,
0A5D59C323AD488600EFD9E9 /* Protocols */,
0A7918F423F5E7EA00772FF4 /* ImageView.swift */, 0A7918F423F5E7EA00772FF4 /* ImageView.swift */,
D272F5F82473163100BD1A8F /* BarButtonItem.swift */, D272F5F82473163100BD1A8F /* BarButtonItem.swift */,
D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */, D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */,
@ -2432,6 +2495,7 @@
32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */, 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */,
D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */, D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */,
DBC4391822442197001AB423 /* CaretView.swift in Sources */, DBC4391822442197001AB423 /* CaretView.swift in Sources */,
C6687441259D92D400F32D13 /* ActionTopNotificationModel.swift in Sources */,
C07065C42395677300FBF997 /* Link.swift in Sources */, C07065C42395677300FBF997 /* Link.swift in Sources */,
0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */, 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */,
D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */, D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */,
@ -2445,6 +2509,7 @@
D264FAAA2440F97600D98315 /* CollectionView.swift in Sources */, D264FAAA2440F97600D98315 /* CollectionView.swift in Sources */,
AAC23FAD24D92A0D009208DF /* ListThreeColumnSpeedTestModel.swift in Sources */, AAC23FAD24D92A0D009208DF /* ListThreeColumnSpeedTestModel.swift in Sources */,
BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */, BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */,
0AE277EC25D2EE310048A38D /* MultiItemDropdownEntryFieldModel.swift in Sources */,
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */,
3265B30424BCA749000D154B /* HeadersH1NoButtonsBodyText.swift in Sources */, 3265B30424BCA749000D154B /* HeadersH1NoButtonsBodyText.swift in Sources */,
AAA7CD69250641F90045B959 /* HeartModel.swift in Sources */, AAA7CD69250641F90045B959 /* HeartModel.swift in Sources */,
@ -2459,6 +2524,7 @@
D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */,
94382086243238D100B43AF3 /* WebViewModel.swift in Sources */, 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */,
D28764F9245A327200CB882D /* TwoLinkView.swift in Sources */, D28764F9245A327200CB882D /* TwoLinkView.swift in Sources */,
0AE277E925D2ED4B0048A38D /* MultiItemDropdownEntryField.swift in Sources */,
D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */, D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */,
0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */, 0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */,
D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */, D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */,
@ -2559,6 +2625,7 @@
D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */, D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */,
014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */, 014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */,
AAC23FAF24D92A1E009208DF /* ListThreeColumnSpeedTest.swift in Sources */, AAC23FAF24D92A1E009208DF /* ListThreeColumnSpeedTest.swift in Sources */,
0A0FEC7425D42A5E00AF2548 /* BaseItemPickerEntryField.swift in Sources */,
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */,
D2ED27ED254B0CE700A1C293 /* ActionPopupModel.swift in Sources */, D2ED27ED254B0CE700A1C293 /* ActionPopupModel.swift in Sources */,
@ -2674,6 +2741,7 @@
BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */, BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */,
017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */, 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */,
323AC96C24C837FF00F8E4C4 /* ListThreeColumnBillChanges.swift in Sources */, 323AC96C24C837FF00F8E4C4 /* ListThreeColumnBillChanges.swift in Sources */,
0A0FEC7825D42A8500AF2548 /* BaseItemPickerEntryFieldModel.swift in Sources */,
D28A837923C7D5BC00DFE4FC /* PageModelProtocol.swift in Sources */, D28A837923C7D5BC00DFE4FC /* PageModelProtocol.swift in Sources */,
D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */, D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */,
017BEB7B236763000024EF95 /* LineModel.swift in Sources */, 017BEB7B236763000024EF95 /* LineModel.swift in Sources */,
@ -2681,6 +2749,7 @@
94C2D9A523872C350006CF46 /* LabelAttributeFontModel.swift in Sources */, 94C2D9A523872C350006CF46 /* LabelAttributeFontModel.swift in Sources */,
011D958724042492000E3791 /* FormFieldProtocol.swift in Sources */, 011D958724042492000E3791 /* FormFieldProtocol.swift in Sources */,
011D95AF2407266E000E3791 /* RadioButtonModel.swift in Sources */, 011D95AF2407266E000E3791 /* RadioButtonModel.swift in Sources */,
D24918F625D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift in Sources */,
D20492A624329CE200A5EED6 /* LoadImageView.swift in Sources */, D20492A624329CE200A5EED6 /* LoadImageView.swift in Sources */,
017BEB7F23676E870024EF95 /* MoleculeObjectMapping.swift in Sources */, 017BEB7F23676E870024EF95 /* MoleculeObjectMapping.swift in Sources */,
D274CA332236A78900B01B62 /* FooterView.swift in Sources */, D274CA332236A78900B01B62 /* FooterView.swift in Sources */,
@ -2738,6 +2807,7 @@
AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */, AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */,
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */,
D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */, D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */,
D29C558A25C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift in Sources */,
8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */, 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */,
BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */, BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */,
AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */, AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */,
@ -2765,6 +2835,7 @@
012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */, 012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */,
D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */, D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */,
0AE14F64238315D2005417F8 /* TextField.swift in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */,
D24918FA25D5ADBB00CAB4B1 /* PageScrolledClosureBehavior.swift in Sources */,
0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */, 0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */,
D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */, D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */,
0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */, 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */,
@ -2777,6 +2848,7 @@
D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */,
AAB7EDF1246ADA2A00E54929 /* ListProgressBarThin.swift in Sources */, AAB7EDF1246ADA2A00E54929 /* ListProgressBarThin.swift in Sources */,
8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */, 8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */,
D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */,
D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */, D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */,
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */,
AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */, AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */,
@ -2840,8 +2912,10 @@
012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */, 012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */,
52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */, 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */,
D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */, D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */,
0AA4D2E125CAEC72008DB32D /* AccessibilityModelProtocol.swift in Sources */,
C003506123AA94CD00B6AC29 /* Button.swift in Sources */, C003506123AA94CD00B6AC29 /* Button.swift in Sources */,
DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */, DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */,
D29C559025C095210082E7D6 /* Video.swift in Sources */,
D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */, D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */,
AA104B1C24474A76004D2810 /* HeadersH2ButtonsModel.swift in Sources */, AA104B1C24474A76004D2810 /* HeadersH2ButtonsModel.swift in Sources */,
0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */, 0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */,
@ -2855,7 +2929,7 @@
D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */, D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */,
BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */, BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */,
D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */, D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */,
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */, 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownFieldModel.swift in Sources */,
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */, 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */,
D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */, D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */,
D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */, D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */,
@ -2867,6 +2941,7 @@
BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */, BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */,
C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */,
D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */, D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */,
D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */,
8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */, 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */,
D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */,
012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */,
@ -2884,6 +2959,7 @@
AA633B3324989ED500731E80 /* HeadersH2PricingTwoRows.swift in Sources */, AA633B3324989ED500731E80 /* HeadersH2PricingTwoRows.swift in Sources */,
01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */, 01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */,
0A6682A22434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift in Sources */, 0A6682A22434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift in Sources */,
D29C558D25C05C990082E7D6 /* BGVideoImageMolecule.swift in Sources */,
EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */, EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */,
0105618D224BBE7700E1557D /* FormValidator.swift in Sources */, 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */,
01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */,
@ -2957,6 +3033,7 @@
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
@ -3022,6 +3099,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

View File

@ -0,0 +1,24 @@
//
// ActionTopNotificationModel.swift
// MVMCoreUI
//
// Created by Murugan, Vimal on 31/12/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers public class ActionTopNotificationModel: ActionModelProtocol {
public static var identifier: String = "topNotification"
public var actionType: String = ActionTopNotificationModel.identifier
public var topNotification: TopNotificationModel
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
public init(topNotification: TopNotificationModel, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.topNotification = topNotification
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
}

View File

@ -18,6 +18,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
public static var identifier: String = "button" public static var identifier: String = "button"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var title: String public var title: String
public var action: ActionModelProtocol public var action: ActionModelProtocol
public var enabled: Bool = true public var enabled: Bool = true
@ -93,27 +94,27 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
//-------------------------------------------------- //--------------------------------------------------
public func enabled_fillColor() -> UIColor? { public func enabled_fillColor() -> UIColor? {
return (inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor (inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor
} }
public func enabled_textColor() -> UIColor? { public func enabled_textColor() -> UIColor? {
return (inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor (inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor
} }
public func enabled_borderColor() -> UIColor? { public func enabled_borderColor() -> UIColor? {
return (inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor (inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor
} }
public func disabled_fillColor() -> UIColor? { public func disabled_fillColor() -> UIColor? {
return (inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor (inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor
} }
public func disabled_textColor() -> UIColor? { public func disabled_textColor() -> UIColor? {
return (inverted ? disabledTextColor_inverted : disabledTextColor)?.uiColor (inverted ? disabledTextColor_inverted : disabledTextColor)?.uiColor
} }
public func disabled_borderColor() -> UIColor? { public func disabled_borderColor() -> UIColor? {
return (inverted ? disabledBorderColor_inverted : disabledBorderColor)?.uiColor (inverted ? disabledBorderColor_inverted : disabledBorderColor)?.uiColor
} }
/// Defines the default appearance for the primary style. /// Defines the default appearance for the primary style.
@ -172,6 +173,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case title case title
case inverted case inverted
case action case action
@ -195,6 +197,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
title = try typeContainer.decode(String.self, forKey: .title) title = try typeContainer.decode(String.self, forKey: .title)
action = try typeContainer.decodeModel(codingKey: .action) action = try typeContainer.decodeModel(codingKey: .action)
@ -252,6 +255,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
try container.encode(inverted, forKey: .inverted) try container.encode(inverted, forKey: .inverted)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(enabledFillColor, forKey: .fillColor) try container.encodeIfPresent(enabledFillColor, forKey: .fillColor)
try container.encodeIfPresent(enabledTextColor, forKey: .textColor) try container.encodeIfPresent(enabledTextColor, forKey: .textColor)
try container.encodeIfPresent(enabledBorderColor, forKey: .borderColor) try container.encodeIfPresent(enabledBorderColor, forKey: .borderColor)

View File

@ -127,7 +127,6 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol {
} }
public func updateCaretSpacing(_ spacing: CGFloat) { public func updateCaretSpacing(_ spacing: CGFloat) {
caretSpacingConstraint?.constant = spacing caretSpacingConstraint?.constant = spacing
} }
@ -151,15 +150,9 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol {
setTitle(model.title, for: .normal) setTitle(model.title, for: .normal)
} }
public func needsToBeConstrained() -> Bool { public func needsToBeConstrained() -> Bool { true }
return true
}
open func horizontalAlignment() -> UIStackView.Alignment { open func horizontalAlignment() -> UIStackView.Alignment { .leading }
return .leading
}
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 10.5 }
return 10.5
}
} }

View File

@ -17,6 +17,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea
public static var identifier: String = "caretLink" public static var identifier: String = "caretLink"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var title: String public var title: String
public var action: ActionModelProtocol public var action: ActionModelProtocol
public var enabledColor: Color = Color(uiColor: .mvmBlack) public var enabledColor: Color = Color(uiColor: .mvmBlack)
@ -41,6 +42,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case backgroundColor case backgroundColor
case accessibilityIdentifier
case title case title
case action case action
case enabledColor_inverted case enabledColor_inverted
@ -60,6 +62,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
title = try typeContainer.decode(String.self, forKey: .title) title = try typeContainer.decode(String.self, forKey: .title)
if let enabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor_inverted) { if let enabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor_inverted) {
@ -94,6 +97,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(title, forKey: .title) try container.encode(title, forKey: .title)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encode(enabled, forKey: .enabledColor) try container.encode(enabled, forKey: .enabledColor)
try container.encodeIfPresent(disabledColor, forKey: .disabledColor) try container.encodeIfPresent(disabledColor, forKey: .disabledColor)

View File

@ -11,18 +11,18 @@ import UIKit
@objcMembers open class Link: Button { @objcMembers open class Link: Button {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Draw
//-------------------------------------------------- //--------------------------------------------------
open override func draw(_ rect: CGRect) { open override func draw(_ rect: CGRect) {
guard let textRect = titleLabel?.frame else { return } guard let textRect = titleLabel?.frame,
let context = UIGraphicsGetCurrentContext()
let context = UIGraphicsGetCurrentContext() else { return }
// Set line to the same color as the text // Set line to the same color as the text
if let color = titleLabel?.textColor?.cgColor { if let color = titleLabel?.textColor?.cgColor {
context?.setStrokeColor(color) context.setStrokeColor(color)
} }
// x should be according to the text, not the button // x should be according to the text, not the button
@ -31,9 +31,9 @@ import UIKit
// Line is 1 point below the text // Line is 1 point below the text
let y = textRect.origin.y + textRect.size.height + 1 let y = textRect.origin.y + textRect.size.height + 1
context?.move(to: CGPoint(x: x, y: y)) context.move(to: CGPoint(x: x, y: y))
context?.addLine(to: CGPoint(x: x + textRect.size.width, y: y)) context.addLine(to: CGPoint(x: x + textRect.size.width, y: y))
context?.strokePath() context.strokePath()
} }
open override var intrinsicContentSize: CGSize { open override var intrinsicContentSize: CGSize {
@ -58,9 +58,7 @@ import UIKit
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
} }
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 31 }
return 31
}
} }
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
@ -69,16 +67,12 @@ extension Link {
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
DispatchQueue.main.async { [weak self] in var width = size
guard let self = self else { return } if MVMCoreGetterUtility.fequal(a: Float.leastNormalMagnitude, b: Float(size)) {
width = MVMCoreUIUtility.getWidth()
var width = size
if MVMCoreGetterUtility.fequal(a: Float.leastNormalMagnitude, b: Float(size)) {
width = MVMCoreUIUtility.getWidth()
}
self.titleLabel?.font = MFStyler.fontB2(forWidth: width)
} }
titleLabel?.font = MFStyler.fontB2(forWidth: width)
} }
open override func setupView() { open override func setupView() {
@ -98,7 +92,5 @@ extension Link {
// MARK: - MVMCoreUIViewConstrainingProtocol // MARK: - MVMCoreUIViewConstrainingProtocol
extension Link: MVMCoreUIViewConstrainingProtocol { extension Link: MVMCoreUIViewConstrainingProtocol {
open func horizontalAlignment() -> UIStackView.Alignment { open func horizontalAlignment() -> UIStackView.Alignment { .leading }
return .leading
}
} }

View File

@ -14,11 +14,10 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public class var identifier: String { public class var identifier: String { "link" }
return "link"
}
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var title: String public var title: String
public var action: ActionModelProtocol public var action: ActionModelProtocol
public var enabled = true public var enabled = true
@ -44,6 +43,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case title case title
case action case action
case enabled case enabled
@ -62,6 +62,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
title = try typeContainer.decode(String.self, forKey: .title) title = try typeContainer.decode(String.self, forKey: .title)
action = try typeContainer.decodeModel(codingKey: .action) action = try typeContainer.decodeModel(codingKey: .action)
@ -95,6 +96,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
try container.encode(title, forKey: .title) try container.encode(title, forKey: .title)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encode(inverted, forKey: .inverted) try container.encode(inverted, forKey: .inverted)
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)

View File

@ -18,7 +18,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
var size = MVMCoreUIUtility.getWidth() var size = MVMCoreUIUtility.getWidth()
var buttonModel: ButtonModel? { var buttonModel: ButtonModel? {
get { return model as? ButtonModel } get { model as? ButtonModel }
} }
/// Need to re-style on set. /// Need to re-style on set.
@ -27,9 +27,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
} }
open var buttonSize: Styler.Button.Size = .standard { open var buttonSize: Styler.Button.Size = .standard {
didSet { didSet { buttonModel?.size = buttonSize }
buttonModel?.size = buttonSize
}
} }
//-------------------------------------------------- //--------------------------------------------------
@ -47,12 +45,12 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
//-------------------------------------------------- //--------------------------------------------------
public var enabledTitleColor: UIColor? { public var enabledTitleColor: UIColor? {
get { return titleColor(for: .normal) } get { titleColor(for: .normal) }
set { setTitleColor(newValue, for: .normal) } set { setTitleColor(newValue, for: .normal) }
} }
public var disabledTitleColor: UIColor? { public var disabledTitleColor: UIColor? {
get { return titleColor(for: .disabled) } get { titleColor(for: .disabled) }
set { setTitleColor(newValue, for: .disabled) } set { setTitleColor(newValue, for: .disabled) }
} }
@ -106,6 +104,11 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
self.disabledTitleColor = disabledTitleColor self.disabledTitleColor = disabledTitleColor
} }
#if DEBUG
// Useful to detect with isHittable when performing UI testing.
isAccessibilityElement = isEnabled
#endif
if isEnabled { if isEnabled {
if let fillColor = buttonModel?.enabledColors.fill { if let fillColor = buttonModel?.enabledColors.fill {
backgroundColor = fillColor backgroundColor = fillColor
@ -128,7 +131,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
} }
private func getInnerPadding() -> CGFloat { private func getInnerPadding() -> CGFloat {
return getHeight() / 2.0 getHeight() / 2.0
} }
private func getHeight() -> CGFloat { private func getHeight() -> CGFloat {
@ -181,9 +184,11 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
guard let model = model as? ButtonModel else { return } guard let model = model as? ButtonModel else { return }
setTitle(model.title, for: .normal) setTitle(model.title, for: .normal)
if let size = model.size { if let size = model.size {
buttonSize = size buttonSize = size
} }
model.updateUI = { [weak self] in model.updateUI = { [weak self] in
MVMCoreDispatchUtility.performBlock(onMainThread: { MVMCoreDispatchUtility.performBlock(onMainThread: {
self?.enableField(model.enabled) self?.enableField(model.enabled)
@ -228,9 +233,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
// MARK: - MVMCoreUIViewConstrainingProtocol // MARK: - MVMCoreUIViewConstrainingProtocol
//-------------------------------------------------- //--------------------------------------------------
open func horizontalAlignment() -> UIStackView.Alignment { open func horizontalAlignment() -> UIStackView.Alignment { .center }
return .center
}
public func enableField(_ enable: Bool) { public func enableField(_ enable: Bool) {
isEnabled = enable isEnabled = enable

View File

@ -43,7 +43,7 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
public override var showError: Bool { public override var showError: Bool {
get { return super.showError } get { super.showError }
set (error) { set (error) {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -80,7 +80,7 @@ import UIKit
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
addSubview(digitField) addSubview(digitField)
digitField.delegate = self digitField.delegate = self
digitField.didDeleteDelegate = self digitField.didDeleteDelegate = self
@ -140,7 +140,7 @@ import UIKit
super.updateView(size) super.updateView(size)
if !MVMCoreGetterUtility.fequal(a: Float(size), b: Float(previousSize)) { if !MVMCoreGetterUtility.fequal(a: Float(size), b: Float(previousSize)) {
var width: CGFloat = 0 var width: CGFloat = 0
var height: CGFloat = 0 var height: CGFloat = 0
var pointSize: CGFloat = 13 var pointSize: CGFloat = 13

View File

@ -75,7 +75,7 @@ import UIKit
private var selectedDigitBox: DigitBox? private var selectedDigitBox: DigitBox?
public var digitEntryModel: DigitEntryFieldModel? { public var digitEntryModel: DigitEntryFieldModel? {
return model as? DigitEntryFieldModel model as? DigitEntryFieldModel
} }
//-------------------------------------------------- //--------------------------------------------------
@ -83,7 +83,7 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
public override var isEnabled: Bool { public override var isEnabled: Bool {
get { return super.isEnabled } get { super.isEnabled }
set (enabled) { set (enabled) {
digitBoxes.forEach { $0.isEnabled = enabled } digitBoxes.forEach { $0.isEnabled = enabled }
super.isEnabled = enabled super.isEnabled = enabled
@ -91,7 +91,7 @@ import UIKit
} }
public override var showError: Bool { public override var showError: Bool {
get { return super.showError } get { super.showError }
set (error) { set (error) {
digitBoxes.forEach { $0.showError = error } digitBoxes.forEach { $0.showError = error }
super.showError = error super.showError = error
@ -99,7 +99,7 @@ import UIKit
} }
public override var isLocked: Bool { public override var isLocked: Bool {
get { return super.isLocked } get { super.isLocked }
set (locked) { set (locked) {
digitBoxes.forEach { $0.isLocked = locked } digitBoxes.forEach { $0.isLocked = locked }
super.isLocked = locked super.isLocked = locked
@ -162,7 +162,7 @@ import UIKit
/// If you're using a MFViewController, you must set this to it /// If you're using a MFViewController, you must set this to it
public override weak var uiTextFieldDelegate: UITextFieldDelegate? { public override weak var uiTextFieldDelegate: UITextFieldDelegate? {
get { return textField.delegate } get { textField.delegate }
set { set {
textField.delegate = self textField.delegate = self
proprietorTextDelegate = newValue proprietorTextDelegate = newValue
@ -357,9 +357,7 @@ import UIKit
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
} }
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 115 }
return 115
}
} }
// MARK: - TextField Delegate // MARK: - TextField Delegate
@ -452,11 +450,11 @@ extension DigitEntryField {
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
} }
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { @objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
} }
} }

View File

@ -12,12 +12,10 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { public override class var identifier: String { "digitTextField" }
return "digitTextField"
}
public var digits: Int = 4 public var digits: Int = 4
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------

View File

@ -1,5 +1,5 @@
// //
// BaseDropdownEntryField.swift // BaseDropdownField.swift
// MVMCoreUI // MVMCoreUI
// //
// Created by Kevin Christiano on 10/23/19. // Created by Kevin Christiano on 10/23/19.
@ -28,8 +28,9 @@ import UIKit
}() }()
public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? { public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? {
return model as? BaseDropdownEntryFieldModel model as? BaseDropdownEntryFieldModel
} }
var additionalData: [AnyHashable: Any]? var additionalData: [AnyHashable: Any]?
//-------------------------------------------------- //--------------------------------------------------
@ -54,7 +55,7 @@ import UIKit
@objc required public init?(coder: NSCoder) { @objc required public init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
fatalError("DropdownEntryField does not support xib.") fatalError("\(String(describing: Self.self)) does not support xib.")
} }
required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
@ -86,15 +87,15 @@ import UIKit
dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData)
} }
public override func dismissFieldInput(_ sender: Any?) { @objc public override func dismissFieldInput(_ sender: Any?) {
performDropdownAction() performDropdownAction()
super.dismissFieldInput(sender) super.dismissFieldInput(sender)
} }
func performDropdownAction() { func performDropdownAction() {
if let baseDropdownEntryFieldModel = baseDropdownEntryFieldModel, let actionModel = baseDropdownEntryFieldModel.action, let actionMap = actionModel.toJSON() { if let baseDropdownEntryFieldModel = baseDropdownEntryFieldModel, let actionModel = baseDropdownEntryFieldModel.action {
let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: baseDropdownEntryFieldModel) let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: baseDropdownEntryFieldModel)
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} }
} }
} }

View File

@ -14,9 +14,7 @@
public var caretView: CaretViewModel? public var caretView: CaretViewModel?
public var action: ActionModelProtocol? public var action: ActionModelProtocol?
public override class var identifier: String { public override class var identifier: String { "" }
return ""
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -29,7 +27,7 @@
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Codec
//-------------------------------------------------- //--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {

View File

@ -23,15 +23,15 @@ import UIKit
}() }()
public var dateFormat: String? { public var dateFormat: String? {
get { dateDropdownModel?.dateFormat }
set { set {
guard let newValue = newValue else { return } guard let newValue = newValue else { return }
dateDropdownModel?.dateFormat = newValue dateDropdownModel?.dateFormat = newValue
} }
get { return dateDropdownModel?.dateFormat }
} }
public var dateDropdownModel: DateDropdownEntryFieldModel? { public var dateDropdownModel: DateDropdownEntryFieldModel? {
return model as? DateDropdownEntryFieldModel model as? DateDropdownEntryFieldModel
} }
//-------------------------------------------------- //--------------------------------------------------
@ -69,6 +69,7 @@ import UIKit
datePicker = UIDatePicker.addDatePicker(to: textField) datePicker = UIDatePicker.addDatePicker(to: textField)
datePicker?.addTarget(self, action: #selector(pickerValueChanged), for: .valueChanged) datePicker?.addTarget(self, action: #selector(pickerValueChanged), for: .valueChanged)
datePicker?.timeZone = NSTimeZone.system datePicker?.timeZone = NSTimeZone.system
textField.inputView = datePicker
UIToolbar.addDismissToolbar(to: textField, delegate: self, action: #selector(dismissFieldInput)) UIToolbar.addDismissToolbar(to: textField, delegate: self, action: #selector(dismissFieldInput))
} }

View File

@ -11,9 +11,7 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { public override class var identifier: String { "dateDropdownEntryField" }
return "dateDropdownEntryField"
}
public var dateFormatter: DateFormatter = { public var dateFormatter: DateFormatter = {
let formatter = DateFormatter() let formatter = DateFormatter()

View File

@ -0,0 +1,85 @@
//
// BaseItemPickerEntryField.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/10/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import UIKit
public typealias TextFieldAndPickerDelegate = (UITextFieldDelegate & UIPickerViewDelegate & UIPickerViewDataSource)
open class BaseItemPickerEntryField: BaseDropdownEntryField, UIPickerViewDelegate, UIPickerViewDataSource {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
open lazy var pickerView = UIPickerView.addPicker(to: textField, delegate: self, dismissAction: #selector(dismissFieldInput))
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
/// Closure passed here will run as picker changes items.
public var observeDropdownChange: ((String, String) -> ())?
/// Closure passed here will run upon dismissing the selection picker.
public var observeDropdownSelection: ((String) -> ())?
/// When selecting for first responder, allow initial selected value to appear in empty text field.
public var setInitialValueInTextField = true
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
@objc open override func setupFieldContainerContent(_ container: UIView) {
super.setupFieldContainerContent(container)
textField.hideBlinkingCaret = true
textField.autocorrectionType = .no
uiTextFieldDelegate = self
}
@objc public func setPickerDelegates(delegate: UIPickerViewDelegate & UIPickerViewDataSource) {
pickerView.delegate = delegate
pickerView.dataSource = delegate
}
//--------------------------------------------------
// MARK: - Molecular
//--------------------------------------------------
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
setPickerDelegates(delegate: self)
}
//--------------------------------------------------
// MARK: - Picker Delegate to Override
//--------------------------------------------------
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 0 }
public func numberOfComponents(in pickerView: UIPickerView) -> Int { 0 }
}
// MARK: - Accessibility
extension BaseItemPickerEntryField {
@objc open override func setAccessibilityString(_ accessibilityString: String?) {
var accessibilityString = accessibilityString ?? ""
if let textPickerItem = MVMCoreUIUtility.hardcodedString(withKey: "textfield_picker_item") {
accessibilityString += textPickerItem
}
textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")"
}
}

View File

@ -0,0 +1,18 @@
//
// BaseItemPickerEntryFieldModel.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/10/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import UIKit
open class BaseItemPickerEntryFieldModel: BaseDropdownEntryFieldModel {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public override class var identifier: String { "" }
}

View File

@ -8,28 +8,16 @@
import UIKit import UIKit
public typealias TextFieldAndPickerDelegate = (UITextFieldDelegate & UIPickerViewDelegate & UIPickerViewDataSource)
open class ItemDropdownEntryField: BaseItemPickerEntryField {
open class ItemDropdownEntryField: BaseDropdownEntryField {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
open var pickerData: [String] = [] open var pickerData: [String] = []
open var pickerView: UIPickerView?
/// When selecting for first responder, allow initial selected value to appear in empty text field.
public var setInitialValueInTextField = true
/// Closure passed here will run as picker changes items.
public var observeDropdownChange: ((String, String)->())?
/// Closure passed here will run upon dismissing the selection picker.
public var observeDropdownSelection: ((String)->())?
public var itemDropdownEntryFieldModel: ItemDropdownEntryFieldModel? { public var itemDropdownEntryFieldModel: ItemDropdownEntryFieldModel? {
return model as? ItemDropdownEntryFieldModel model as? ItemDropdownEntryFieldModel
} }
//-------------------------------------------------- //--------------------------------------------------
@ -61,26 +49,13 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
@objc open override func setupFieldContainerContent(_ container: UIView) { /// Sets the textField with the first value of the available picker data.
super.setupFieldContainerContent(container)
pickerView = UIPickerView.addPicker(to: textField, delegate: self, dismissAction: #selector(dismissFieldInput))
textField.hideBlinkingCaret = true
textField.autocorrectionType = .no
uiTextFieldDelegate = self
}
@objc public func setPickerDelegates(delegate: UIPickerViewDelegate & UIPickerViewDataSource) {
pickerView?.delegate = delegate
pickerView?.dataSource = delegate
}
@objc private func setInitialValueFromPicker() { @objc private func setInitialValueFromPicker() {
guard !pickerData.isEmpty else { return } guard !pickerData.isEmpty else { return }
if setInitialValueInTextField, let pickerIndex = pickerView?.selectedRow(inComponent: 0) { if setInitialValueInTextField {
let pickerIndex = pickerView.selectedRow(inComponent: 0)
observeDropdownChange?(text ?? "", pickerData[pickerIndex]) observeDropdownChange?(text ?? "", pickerData[pickerIndex])
text = pickerData[pickerIndex] text = pickerData[pickerIndex]
itemDropdownEntryFieldModel?.selectedIndex = pickerIndex itemDropdownEntryFieldModel?.selectedIndex = pickerIndex
@ -98,9 +73,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
guard !pickerData.isEmpty else { return } guard !pickerData.isEmpty else { return }
if let pickerIndex = pickerView?.selectedRow(inComponent: 0) { observeDropdownSelection?(pickerData[pickerView.selectedRow(inComponent: 0)])
observeDropdownSelection?(pickerData[pickerIndex])
}
} }
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
@ -109,23 +82,21 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
guard let model = model as? ItemDropdownEntryFieldModel else { return } guard let model = model as? ItemDropdownEntryFieldModel else { return }
pickerData = model.options pickerData = model.options
setPickerDelegates(delegate: self)
if let pickerView = pickerView, let index = model.selectedIndex { if let index = model.selectedIndex {
self.pickerView.selectRow(index, inComponent: 0, animated: false)
self.pickerView(pickerView, didSelectRow: index, inComponent: 0) self.pickerView(pickerView, didSelectRow: index, inComponent: 0)
} }
} }
}
// MARK:- Base Picker Delegate //--------------------------------------------------
extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { // MARK: - Picker Delegate
//--------------------------------------------------
@objc public func numberOfComponents(in pickerView: UIPickerView) -> Int { @objc public override func numberOfComponents(in pickerView: UIPickerView) -> Int { 1 }
return 1
}
@objc public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { @objc public override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count pickerData.count
} }
@objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { @objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
@ -142,18 +113,3 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
itemDropdownEntryFieldModel?.selectedIndex = row itemDropdownEntryFieldModel?.selectedIndex = row
} }
} }
// MARK: - Accessibility
extension ItemDropdownEntryField {
@objc open override func setAccessibilityString(_ accessibilityString: String?) {
var accessibilityString = accessibilityString ?? ""
if let textPickerItem = MVMCoreUIUtility.hardcodedString(withKey: "textfield_picker_item") {
accessibilityString += textPickerItem
}
textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")"
}
}

View File

@ -6,22 +6,24 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
@objcMembers open class ItemDropdownEntryFieldModel: BaseDropdownEntryFieldModel { @objcMembers open class ItemDropdownEntryFieldModel: BaseItemPickerEntryFieldModel {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { public override class var identifier: String { "dropDown" }
return "dropDown"
}
public var options: [String] = [] public var options: [String] = []
public var selectedIndex: Int? public var selectedIndex: Int?
//--------------------------------------------------
// MARK: - Validation
//--------------------------------------------------
public override func formFieldValue() -> AnyHashable? { public override func formFieldValue() -> AnyHashable? {
guard !options.isEmpty, guard !options.isEmpty,
let index = selectedIndex let index = selectedIndex
else { return nil } else { return nil }
return options[index] return options[index]
} }
@ -34,7 +36,7 @@
case options case options
case selectedIndex case selectedIndex
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -47,10 +49,7 @@
if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) { if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) {
self.selectedIndex = selectedIndex self.selectedIndex = selectedIndex
} baseValue = options.indices.contains(selectedIndex) ? options[selectedIndex] : nil
if let index = selectedIndex {
baseValue = options.indices.contains(index) ? options[index] : nil
} }
} }

View File

@ -0,0 +1,144 @@
//
// MultiItemDropdownEndryField.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/9/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import UIKit
open class MultiItemDropdownEntryField: BaseItemPickerEntryField {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
/// Datasource of the picker view.
open var pickerComponents: [[String]] {
dropdownModel?.components ?? [[]]
}
public var dropdownModel: MultiItemDropdownEntryFieldModel? {
model as? MultiItemDropdownEntryFieldModel
}
/// The number of components available
public var componentCount: Int {
pickerComponents.count
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
@objc public override init(frame: CGRect) {
super.init(frame: frame)
}
@objc public convenience init() {
self.init(frame: .zero)
}
@objc required public init?(coder: NSCoder) {
fatalError("MultiItemDropdownEntryField init(coder:) has not been implemented")
}
required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.init(model: model, delegateObject, additionalData)
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Sets the textField with the first value of the available picker data.
@objc private func setInitialValueFromPicker() {
guard setInitialValueInTextField,
!pickerComponents.isEmpty,
let rowText = dropdownModel?.selectedRowText
else { return }
// Update observing function and update text UI.
observeDropdownChange?(text ?? "", rowText)
text = rowText
// Set row index value of selected component.
for component in 0..<componentCount {
let pickerIndex = pickerView.selectedRow(inComponent: component)
dropdownModel?.selectedIndexes[component] = pickerIndex
}
}
func pickerHasComponent(_ index: Int) -> Bool {
!pickerComponents.isEmpty && !pickerComponents[index].isEmpty
}
//--------------------------------------------------
// MARK: - TextField Observation
//--------------------------------------------------
/// Observing action of the textfield for initial interaction with the picker.
@objc override func startEditing() {
super.startEditing()
setInitialValueFromPicker()
}
/// Observing action for when the user has ended inputting with the picker.
@objc override func endInputing() {
super.endInputing()
guard !pickerComponents.isEmpty,
let rowText = dropdownModel?.selectedRowText
else { return }
// update observing function with most recent selection.
observeDropdownSelection?(rowText)
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? MultiItemDropdownEntryFieldModel else { return }
// Select initial rows if selectedIndexes retains value.
for (component, row) in model.selectedIndexes {
self.pickerView.selectRow(row, inComponent: component, animated: false)
self.pickerView(pickerView, didSelectRow: row, inComponent: component)
}
}
//--------------------------------------------------
// MARK: - Picker Delegate
//--------------------------------------------------
@objc public override func numberOfComponents(in pickerView: UIPickerView) -> Int { componentCount }
@objc public override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
pickerComponents[component].count
}
@objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
guard pickerHasComponent(component) else { return nil }
return pickerComponents[component][row]
}
@objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
guard pickerHasComponent(component) else { return }
let oldText = text ?? ""
dropdownModel?.selectedIndexes[component] = row
let newText = dropdownModel?.selectedRowText
observeDropdownChange?(oldText, newText ?? "")
text = newText
}
}

View File

@ -0,0 +1,126 @@
//
// MultiItemDropdownEntryFieldModel.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/9/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class MultiItemDropdownEntryFieldModel: BaseItemPickerEntryFieldModel {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public override class var identifier: String { "multiDropdown" }
public var components: [[String]] = [[]]
public var selectedIndexes: [Int: Int] = [:]
public var delimiters: [Int: String] = [:]
//--------------------------------------------------
// MARK: - Validation
//--------------------------------------------------
public override func formFieldValue() -> AnyHashable? {
guard !components.isEmpty && !selectedIndexes.isEmpty else { return nil }
return selectedRowText
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// - parameter index: The index of the delimiter.
/// - returns: The delimiter for a given index. Defaults to whitespace for valid index if no delimiters is provided. If invalid index, empty string.
public func delimiter(for index: Int) -> String {
guard index != components.count - 1 else { return "" }
return delimiters[index, default: " "]
}
/// A string of the picker row concatenated by whitespace or delimiters if provided.
public var selectedRowText: String {
var text = ""
for i in 0..<components.count {
let pickerIndex = selectedIndexes[i] ?? 0
text += components[i][pickerIndex] + delimiter(for: i)
}
return text
}
public var selectedIndexesArray: [Int] {
var indexArray: [Int] = []
for i in 0..<selectedIndexes.count {
guard let selectIndex = selectedIndexes[i] else { return [] }
indexArray.append(selectIndex)
}
return indexArray
}
public var delimiterArray: [String] {
var array: [String] = []
for i in 0..<delimiters.count {
guard let delimiterIndex = delimiters[i] else { return [] }
array.append(delimiterIndex)
}
return array
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case components
case selectedIndexes
case delimiters
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
try super.init(from: decoder)
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
components = try typeContainer.decode([[String]].self, forKey: .components)
if let delimiters = try typeContainer.decodeIfPresent([String].self, forKey: .delimiters) {
for (index, delimiter) in delimiters.enumerated() {
self.delimiters[index] = delimiter
}
}
if let indexes = try typeContainer.decodeIfPresent([Int].self, forKey: .selectedIndexes) {
for (component, index) in indexes.enumerated() {
self.selectedIndexes[component] = index
}
baseValue = selectedRowText
}
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(components, forKey: .components)
try container.encode(selectedIndexesArray, forKey: .selectedIndexes)
try container.encodeIfPresent(delimiterArray, forKey: .delimiters)
}
}

View File

@ -36,7 +36,7 @@ import UIKit
label.setContentCompressionResistancePriority(.required, for: .vertical) label.setContentCompressionResistancePriority(.required, for: .vertical)
return label return label
}() }()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Delegate // MARK: - Delegate
//-------------------------------------------------- //--------------------------------------------------
@ -58,71 +58,74 @@ import UIKit
/// Toggles enabled (original) or disabled UI. /// Toggles enabled (original) or disabled UI.
public var isEnabled: Bool { public var isEnabled: Bool {
get { return entryFieldContainer.isEnabled } get { entryFieldContainer.isEnabled }
set (enabled) { set (enabled) {
self.titleLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3 titleLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
self.feedbackLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3 feedbackLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
self.entryFieldContainer.isEnabled = enabled entryFieldContainer.isEnabled = enabled
self.entryFieldModel?.enabled = enabled entryFieldModel?.enabled = enabled
} }
} }
/// Toggles error or original UI. /// Toggles error or original UI.
public var showError: Bool { public var showError: Bool {
get { return entryFieldContainer.showError } get { entryFieldContainer.showError }
set (error) { set (error) {
self.feedback = error ? entryFieldModel?.errorMessage : entryFieldModel?.feedback feedback = error ? errorMessage : entryFieldModel?.feedback
self.feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack
self.entryFieldContainer.showError = error entryFieldContainer.showError = error
self.entryFieldModel?.showError = error entryFieldModel?.showError = error
} }
} }
var errorMessage: String? {
entryFieldModel?.dynamicErrorMessage ?? entryFieldModel?.errorMessage
}
/// Toggles original or locked UI. /// Toggles original or locked UI.
public var isLocked: Bool { public var isLocked: Bool {
get { return entryFieldContainer.isLocked } get { entryFieldContainer.isLocked }
set (locked) { set (locked) {
self.entryFieldContainer.isLocked = locked entryFieldContainer.isLocked = locked
self.entryFieldModel?.locked = locked entryFieldModel?.locked = locked
} }
} }
/// Toggles selected or original (unselected) UI. /// Toggles selected or original (unselected) UI.
public var isSelected: Bool { public var isSelected: Bool {
get { return entryFieldContainer.isSelected } get { entryFieldContainer.isSelected }
set (selected) { set (selected) {
self.entryFieldContainer.isSelected = selected entryFieldContainer.isSelected = selected
self.entryFieldModel?.selected = selected entryFieldModel?.selected = selected
} }
} }
/// Sets the text of titleLabel /// Sets the text of titleLabel
public var title: String? { public var title: String? {
get { return titleLabel.text } get { titleLabel.text }
set (newText) { set {
titleLabel.text = newText titleLabel.text = newValue
setAccessibilityString(newText) setAccessibilityString(newValue)
} }
} }
/// Override this to conveniently get/set the textfield(s). /// Override this to conveniently get/set the textfield(s).
public var text: String? { public var text: String? {
get { return nil } get { nil }
set { fatalError("You MUST override EntryField's 'text' variable in your subclass.") } set { fatalError("You MUST override EntryField's 'text' variable in your subclass.") }
} }
/// Sets feedback text in the textField. /// Sets feedback text in the textField.
public var feedback: String? { public var feedback: String? {
get { return feedbackLabel.text } get { feedbackLabel.text }
set (newFeedback) { set {
feedbackLabel.text = newFeedback feedbackLabel.text = newValue
feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true
entryFieldContainer.refreshUI(updateMoleculeLayout: true)
} }
} }
public var entryFieldModel: EntryFieldModel? { public var entryFieldModel: EntryFieldModel? {
return model as? EntryFieldModel model as? EntryFieldModel
} }
//-------------------------------------------------- //--------------------------------------------------
@ -219,7 +222,7 @@ import UIKit
entryFieldContainer.refreshUI() entryFieldContainer.refreshUI()
} }
/// Intended to add the interactive content (i.e. textField) to the entryFieldContainer. /// Intended to add the interactive content (i.e. textField) to the entryFieldContainer.
@objc open func setupFieldContainerContent(_ container: UIView) { @objc open func setupFieldContainerContent(_ container: UIView) {
// To Be Overriden // To Be Overriden
} }
@ -306,16 +309,30 @@ import UIKit
if self.isSelected { if self.isSelected {
self.updateValidation(model.isValid ?? true) self.updateValidation(model.isValid ?? true)
} else if model.isValid ?? true && self.showError { } else if model.isValid ?? true && self.showError {
self.showError = false self.showError = false
} }
}) })
} }
model.updateUIDynamicError = { [weak self] in
MVMCoreDispatchUtility.performBlock(onMainThread: {
guard let self = self else { return }
let validState = model.isValid ?? false
self.updateValidation(validState)
if !validState && model.shouldClearText {
self.text = ""
model.shouldClearText = false
}
})
}
title = model.title title = model.title
feedback = model.feedback feedback = model.feedback
isEnabled = model.enabled isEnabled = model.enabled
entryFieldContainer.disableAllBorders = model.hideBorders entryFieldContainer.disableAllBorders = model.hideBorders
accessibilityIdentifier = model.accessibilityIdentifier ?? model.fieldKey
if let isLocked = model.locked { if let isLocked = model.locked {
self.isLocked = isLocked self.isLocked = isLocked
@ -327,9 +344,7 @@ import UIKit
} }
} }
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 115 }
return 115
}
} }
// MARK: - Accessibility // MARK: - Accessibility

View File

@ -14,13 +14,19 @@ import Foundation
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public class var identifier: String { public class var identifier: String { "" }
return ""
}
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var title: String? public var title: String?
public var feedback: String? public var feedback: String?
public var shouldClearText: Bool = false
public var dynamicErrorMessage: String? {
didSet {
isValid = dynamicErrorMessage?.isEmpty ?? true
updateUIDynamicError?()
}
}
public var errorMessage: String? public var errorMessage: String?
public var errorTextColor: Color? public var errorTextColor: Color?
public var enabled: Bool = true public var enabled: Bool = true
@ -41,6 +47,9 @@ import Foundation
/// Temporary binding mechanism for the view to update on enable changes. /// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: ActionBlock? public var updateUI: ActionBlock?
// TODO: Remove once updateUI is fixed with isSelected
public var updateUIDynamicError: ActionBlock?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------
@ -48,6 +57,7 @@ import Foundation
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case title case title
case enabled case enabled
case feedback case feedback
@ -67,6 +77,9 @@ import Foundation
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? {
if dynamicErrorMessage != nil {
dynamicErrorMessage = nil
}
return text return text
} }
@ -96,6 +109,7 @@ import Foundation
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
title = try typeContainer.decodeIfPresent(String.self, forKey: .title) title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback) feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback)
errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage) errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage)
@ -117,6 +131,7 @@ import Foundation
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(title, forKey: .title) try container.encodeIfPresent(title, forKey: .title)
try container.encodeIfPresent(feedback, forKey: .feedback) try container.encodeIfPresent(feedback, forKey: .feedback)
try container.encodeIfPresent(text, forKey: .text) try container.encodeIfPresent(text, forKey: .text)
@ -125,9 +140,9 @@ import Foundation
try container.encodeIfPresent(selected, forKey: .selected) try container.encodeIfPresent(selected, forKey: .selected)
try container.encodeIfPresent(errorTextColor, forKey: .errorTextColor) try container.encodeIfPresent(errorTextColor, forKey: .errorTextColor)
try container.encodeIfPresent(errorMessage, forKey: .errorMessage) try container.encodeIfPresent(errorMessage, forKey: .errorMessage)
try container.encode(enabled, forKey: .enabled)
try container.encode(hideBorders, forKey: .hideBorders)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encode(enabled, forKey: .enabled)
try container.encode(hideBorders, forKey: .hideBorders)
} }
} }

View File

@ -31,7 +31,7 @@ import MVMCore
/// If you're using a MFViewController, you must set this to it /// If you're using a MFViewController, you must set this to it
public override weak var uiTextFieldDelegate: UITextFieldDelegate? { public override weak var uiTextFieldDelegate: UITextFieldDelegate? {
get { return textField.delegate } get { textField.delegate }
set { set {
textField.delegate = self textField.delegate = self
proprietorTextDelegate = newValue proprietorTextDelegate = newValue
@ -44,7 +44,7 @@ import MVMCore
/// Formats the MDN when setting and removes format of MDN when reading. /// Formats the MDN when setting and removes format of MDN when reading.
public var mdn: String? { public var mdn: String? {
get { return MVMCoreUIUtility.removeMdnFormat(text) } get { MVMCoreUIUtility.removeMdnFormat(text) }
set { text = MVMCoreUIUtility.formatMdn(newValue) } set { text = MVMCoreUIUtility.formatMdn(newValue) }
} }
@ -160,9 +160,9 @@ import MVMCore
// Sometimes user add extra 1 in front of mdn in their address book // Sometimes user add extra 1 in front of mdn in their address book
if isNationalMDN, if isNationalMDN,
let unformedMDN = unformattedMDN, let unformedMDN = unformattedMDN,
unformedMDN.count == 11, unformedMDN.count == 11,
unformedMDN[(unformedMDN.index(unformedMDN.startIndex, offsetBy: 0))] == "1" { unformedMDN[(unformedMDN.index(unformedMDN.startIndex, offsetBy: 0))] == "1" {
let startIndex = unformedMDN.index(unformedMDN.startIndex, offsetBy: 1) let startIndex = unformedMDN.index(unformedMDN.startIndex, offsetBy: 1)
unformattedMDN = String(unformedMDN[startIndex...]) unformattedMDN = String(unformedMDN[startIndex...])
@ -211,17 +211,14 @@ import MVMCore
} }
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
} }
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { @objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
} }
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool { @objc public func textFieldShouldClear(_ textField: UITextField) -> Bool {
proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true
return proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true
} }
} }

View File

@ -11,7 +11,5 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { public override class var identifier: String { "mdnEntryField" }
return "mdnEntryField"
}
} }

View File

@ -54,16 +54,14 @@ import UIKit
/// Validate when user resigns editing. Default: true /// Validate when user resigns editing. Default: true
public var validateWhenDoneEditing: Bool = true public var validateWhenDoneEditing: Bool = true
public var textEntryFieldModel: TextEntryFieldModel? { public var textEntryFieldModel: TextEntryFieldModel? { model as? TextEntryFieldModel }
return model as? TextEntryFieldModel
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Computed Properties // MARK: - Computed Properties
//-------------------------------------------------- //--------------------------------------------------
public override var isEnabled: Bool { public override var isEnabled: Bool {
get { return super.isEnabled } get { super.isEnabled }
set (enabled) { set (enabled) {
super.isEnabled = enabled super.isEnabled = enabled
@ -77,7 +75,7 @@ import UIKit
} }
public override var showError: Bool { public override var showError: Bool {
get { return super.showError } get { super.showError }
set (error) { set (error) {
if error { if error {
@ -96,16 +94,16 @@ import UIKit
/// The text of this TextField. /// The text of this TextField.
open override var text: String? { open override var text: String? {
get { return textField.text } get { textField.text }
set { set {
textField.text = newValue
textEntryFieldModel?.text = newValue textEntryFieldModel?.text = newValue
textField.text = newValue
} }
} }
/// Placeholder access for the TextField. /// Placeholder access for the TextField.
public var placeholder: String? { public var placeholder: String? {
get { return textField.placeholder } get { textField.placeholder }
set { textField.placeholder = newValue } set { textField.placeholder = newValue }
} }
@ -133,7 +131,7 @@ import UIKit
/// If you're using a ViewController, you must set this to it /// If you're using a ViewController, you must set this to it
public weak var uiTextFieldDelegate: UITextFieldDelegate? { public weak var uiTextFieldDelegate: UITextFieldDelegate? {
get { return textField.delegate } get { textField.delegate }
set { textField.delegate = newValue } set { textField.delegate = newValue }
} }
@ -222,9 +220,7 @@ import UIKit
@discardableResult @discardableResult
@objc override open func resignFirstResponder() -> Bool { @objc override open func resignFirstResponder() -> Bool {
if validateWhenDoneEditing { if validateWhenDoneEditing { validateText() }
validateText()
}
textField.resignFirstResponder() textField.resignFirstResponder()
isSelected = false isSelected = false
return true return true
@ -239,6 +235,11 @@ import UIKit
/// Executes on UITextField.textDidBeginEditingNotification /// Executes on UITextField.textDidBeginEditingNotification
@objc override func startEditing() { @objc override func startEditing() {
super.startEditing() super.startEditing()
if textEntryFieldModel?.clearTextOnTap ?? false {
text = ""
}
textField.becomeFirstResponder() textField.becomeFirstResponder()
} }
@ -257,14 +258,33 @@ import UIKit
showError = false showError = false
return return
} }
if let isValid = textEntryFieldModel?.isValid { if let isValid = textEntryFieldModel?.isValid {
self.isValid = isValid self.isValid = isValid
} }
regexTextFieldOutputIfAvailable()
shouldShowError(!isValid) shouldShowError(!isValid)
} }
func regexTextFieldOutputIfAvailable() {
if let regex = textEntryFieldModel?.displayFormat,
let mask = textEntryFieldModel?.displayMask,
let finalText = text {
let range = NSRange(finalText.startIndex..., in: finalText)
if let regex = try? NSRegularExpression(pattern: regex) {
let maskedText = regex.stringByReplacingMatches(in: finalText,
range: range,
withTemplate: mask)
textField.text = maskedText
}
}
}
@objc public func dismissFieldInput(_ sender: Any?) { @objc public func dismissFieldInput(_ sender: Any?) {
resignFirstResponder() resignFirstResponder()
} }
@ -317,6 +337,10 @@ import UIKit
case .password, .secure: case .password, .secure:
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
case .numberSecure:
textField.isSecureTextEntry = true
textField.keyboardType = .numberPad
case .number: case .number:
textField.keyboardType = .numberPad textField.keyboardType = .numberPad
@ -326,17 +350,20 @@ import UIKit
case .phone: case .phone:
textField.keyboardType = .phonePad textField.keyboardType = .phonePad
default: default: break
break
} }
// Override the preset keyboard set in type.
if let keyboardType = model.assignKeyboardType() {
textField.keyboardType = keyboardType
}
textField.accessibilityIdentifier = model.accessibilityIdentifier
uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate
observingTextFieldDelegate = delegateObject?.observingTextFieldDelegate observingTextFieldDelegate = delegateObject?.observingTextFieldDelegate
setupTextFieldToolbar() setupTextFieldToolbar()
if isSelected { if isSelected { startEditing() }
startEditing()
}
} }
} }

View File

@ -16,6 +16,7 @@
case password case password
case secure case secure
case number case number
case numberSecure
case email case email
case text case text
case phone case phone
@ -25,15 +26,69 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { public override class var identifier: String { "textField" }
return "textField"
}
public var placeholder: String? public var placeholder: String?
public var enabledTextColor: Color = Color(uiColor: .mvmBlack) public var enabledTextColor: Color = Color(uiColor: .mvmBlack)
public var disabledTextColor: Color = Color(uiColor: .mvmCoolGray3) public var disabledTextColor: Color = Color(uiColor: .mvmCoolGray3)
public var textAlignment: NSTextAlignment = .left public var textAlignment: NSTextAlignment = .left
public var keyboardOverride: String?
public var type: EntryType? public var type: EntryType?
public var clearTextOnTap: Bool = false
public var displayFormat: String?
public var displayMask: String?
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Reads the keyboardOverride set by server and returns the keyboard type associated with it.
func assignKeyboardType() -> UIKeyboardType? {
guard let keyboardType = keyboardOverride else { return nil }
var typeInt = 0
switch keyboardType {
case "asciiCapable":
typeInt = 1 // Displays a keyboard which can enter ASCII characters
case "numbersAndPunctuation":
typeInt = 2 // Numbers and assorted punctuation.
case "URL":
typeInt = 3 // A type optimized for URL entry (shows . / .com prominently).
case "numberPad":
typeInt = 4 // A number pad with locale-appropriate digits (0-9, ۰-۹, -, etc.). Suitable for PIN entry.
case "phonePad":
typeInt = 5 // A phone pad (1-9, *, 0, #, with letters under the numbers).
case "namePhonePad":
typeInt = 6 // A type optimized for entering a person's name or phone number.
case "emailAddress":
typeInt = 7 // A type optimized for multiple email address entry (shows space @ . prominently).
case "decimalPad":
typeInt = 8 // A number pad with a decimal point.
case "twitter":
typeInt = 9 // A type optimized for twitter text entry (easy access to @ #)
case "webSearch":
typeInt = 10 // A default keyboard type with URL-oriented addition (shows space . prominently).
case "asciiCapableNumberPad":
typeInt = 11 // A number pad (0-9) that will always be ASCII digits.
default:
typeInt = 0 // Default type for the current input method.
}
return UIKeyboardType(rawValue: typeInt)
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -44,7 +99,11 @@
case textAlignment case textAlignment
case enabledTextColor case enabledTextColor
case disabledTextColor case disabledTextColor
case keyboardOverride
case type case type
case clearTextOnTap
case displayFormat
case displayMask
} }
//-------------------------------------------------- //--------------------------------------------------
@ -56,8 +115,15 @@
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder) placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder)
displayFormat = try typeContainer.decodeIfPresent(String.self, forKey: .displayFormat)
keyboardOverride = try typeContainer.decodeIfPresent(String.self, forKey: .keyboardOverride)
displayMask = try typeContainer.decodeIfPresent(String.self, forKey: .displayMask)
type = try typeContainer.decodeIfPresent(EntryType.self, forKey: .type) type = try typeContainer.decodeIfPresent(EntryType.self, forKey: .type)
if let clearTextOnTap = try typeContainer.decodeIfPresent(Bool.self, forKey: .clearTextOnTap) {
self.clearTextOnTap = clearTextOnTap
}
if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor) { if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor) {
self.enabledTextColor = enabledTextColor self.enabledTextColor = enabledTextColor
} }
@ -76,8 +142,12 @@
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(placeholder, forKey: .placeholder) try container.encodeIfPresent(placeholder, forKey: .placeholder)
try container.encodeIfPresent(textAlignment, forKey: .textAlignment) try container.encodeIfPresent(textAlignment, forKey: .textAlignment)
try container.encodeIfPresent(type, forKey: .type)
try container.encodeIfPresent(displayFormat, forKey: .displayFormat)
try container.encodeIfPresent(keyboardOverride, forKey: .keyboardOverride)
try container.encodeIfPresent(displayMask, forKey: .displayMask)
try container.encode(enabledTextColor, forKey: .enabledTextColor) try container.encode(enabledTextColor, forKey: .enabledTextColor)
try container.encode(disabledTextColor, forKey: .disabledTextColor) try container.encode(disabledTextColor, forKey: .disabledTextColor)
try container.encodeIfPresent(type, forKey: .type) try container.encode(clearTextOnTap, forKey: .clearTextOnTap)
} }
} }

View File

@ -31,11 +31,11 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
//-------------------------------------------------- //--------------------------------------------------
public var textViewEntryFieldModel: TextViewEntryFieldModel? { public var textViewEntryFieldModel: TextViewEntryFieldModel? {
return model as? TextViewEntryFieldModel model as? TextViewEntryFieldModel
} }
public override var isEnabled: Bool { public override var isEnabled: Bool {
get { return super.isEnabled } get { super.isEnabled }
set (enabled) { set (enabled) {
super.isEnabled = enabled super.isEnabled = enabled
@ -53,7 +53,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
} }
public override var showError: Bool { public override var showError: Bool {
get { return super.showError } get { super.showError }
set (error) { set (error) {
if error { if error {
@ -68,7 +68,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
/// The text of this textView. /// The text of this textView.
open override var text: String? { open override var text: String? {
get { return textViewEntryFieldModel?.text } get { textViewEntryFieldModel?.text }
set { set {
textView.text = newValue textView.text = newValue
textViewEntryFieldModel?.text = newValue textViewEntryFieldModel?.text = newValue
@ -77,7 +77,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
/// Placeholder access for the textView. /// Placeholder access for the textView.
public var placeholder: String? { public var placeholder: String? {
get { return textViewEntryFieldModel?.placeholder } get { textViewEntryFieldModel?.placeholder }
set { set {
textView.placeholder = newValue ?? "" textView.placeholder = newValue ?? ""
textViewEntryFieldModel?.placeholder = newValue textViewEntryFieldModel?.placeholder = newValue
@ -127,7 +127,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
/// If you're using a ViewController, you must set this to it /// If you're using a ViewController, you must set this to it
public weak var uiTextViewDelegate: UITextViewDelegate? { public weak var uiTextViewDelegate: UITextViewDelegate? {
get { return textView.delegate } get { textView.delegate }
set { textView.delegate = newValue } set { textView.delegate = newValue }
} }
@ -149,17 +149,17 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
@objc open override func setupFieldContainerContent(_ container: UIView) { @objc open override func setupFieldContainerContent(_ container: UIView) {
container.addSubview(textView) container.addSubview(textView)
topConstraint = textView.topAnchor.constraint(equalTo: container.topAnchor, constant: Padding.Three) topConstraint = textView.topAnchor.constraint(equalTo: container.topAnchor, constant: Padding.Three)
leadingConstraint = textView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: Padding.Three) leadingConstraint = textView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: Padding.Three)
trailingConstraint = container.trailingAnchor.constraint(equalTo: textView.trailingAnchor, constant: Padding.Three) trailingConstraint = container.trailingAnchor.constraint(equalTo: textView.trailingAnchor, constant: Padding.Three)
bottomConstraint = container.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: Padding.Three) bottomConstraint = container.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: Padding.Three)
topConstraint?.isActive = true topConstraint?.isActive = true
leadingConstraint?.isActive = true leadingConstraint?.isActive = true
trailingConstraint?.isActive = true trailingConstraint?.isActive = true
bottomConstraint?.isActive = true bottomConstraint?.isActive = true
heightConstraint = textView.heightAnchor.constraint(equalToConstant: 0) heightConstraint = textView.heightAnchor.constraint(equalToConstant: 0)
accessibilityElements = [titleLabel, textView, feedbackLabel] accessibilityElements = [titleLabel, textView, feedbackLabel]
} }
@ -203,7 +203,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
/// Executes on UITextView.textDidEndEditingNotification /// Executes on UITextView.textDidEndEditingNotification
@objc override func endInputing() { @objc override func endInputing() {
super.endInputing() super.endInputing()
// Don't show error till user starts typing. // Don't show error till user starts typing.
guard text?.count ?? 0 != 0 else { guard text?.count ?? 0 != 0 else {
showError = false showError = false
@ -241,6 +241,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
textView.isEditable = model.editable textView.isEditable = model.editable
textView.textAlignment = model.textAlignment textView.textAlignment = model.textAlignment
textView.accessibilityIdentifier = model.accessibilityIdentifier
textView.textColor = model.enabled ? model.enabledTextColor.uiColor : model.disabledTextColor.uiColor textView.textColor = model.enabled ? model.enabledTextColor.uiColor : model.disabledTextColor.uiColor
textView.font = model.fontStyle.getFont() textView.font = model.fontStyle.getFont()
textView.placeholder = model.placeholder ?? "" textView.placeholder = model.placeholder ?? ""
@ -252,13 +253,17 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
case .secure, .password: case .secure, .password:
textView.isSecureTextEntry = true textView.isSecureTextEntry = true
case .numberSecure:
textView.isSecureTextEntry = true
textView.keyboardType = .numberPad
case .number: case .number:
textView.keyboardType = .numberPad textView.keyboardType = .numberPad
case .email: case .email:
textView.keyboardType = .emailAddress textView.keyboardType = .emailAddress
default: break default: break
} }
/// No point in configuring if the TextView is Read-only. /// No point in configuring if the TextView is Read-only.

View File

@ -14,9 +14,7 @@ class TextViewEntryFieldModel: TextEntryFieldModel {
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { public override class var identifier: String { "textView" }
return "textView"
}
public var accessibilityText: String? public var accessibilityText: String?
public var fontStyle: Styler.Font = Styler.Font.RegularBodyLarge public var fontStyle: Styler.Font = Styler.Font.RegularBodyLarge
@ -25,7 +23,7 @@ class TextViewEntryFieldModel: TextEntryFieldModel {
public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro
public var editable: Bool = true public var editable: Bool = true
public var showsPlaceholder: Bool = false public var showsPlaceholder: Bool = false
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------

View File

@ -25,7 +25,7 @@ import MVMCore
var delegateObject: MVMCoreUIDelegateObject? var delegateObject: MVMCoreUIDelegateObject?
public var checkboxModel: CheckboxModel? { public var checkboxModel: CheckboxModel? {
return model as? CheckboxModel model as? CheckboxModel
} }
public static let defaultHeightWidth: CGFloat = 18.0 public static let defaultHeightWidth: CGFloat = 18.0
@ -59,7 +59,7 @@ import MVMCore
/// Retrieves ideeal radius value to curve square into a circle. /// Retrieves ideeal radius value to curve square into a circle.
public var cornerRadiusValue: CGFloat { public var cornerRadiusValue: CGFloat {
return bounds.size.height / 2 bounds.size.height / 2
} }
/// Action Block called when the switch is selected. /// Action Block called when the switch is selected.
@ -102,23 +102,17 @@ import MVMCore
/// Color of the check mark. /// Color of the check mark.
public var checkColor: UIColor = .mvmBlack { public var checkColor: UIColor = .mvmBlack {
didSet { didSet { setShapeLayerStrokeColor(checkColor) }
setShapeLayerStrokeColor(checkColor)
}
} }
/// Border width of the checkbox /// Border width of the checkbox
public var borderWidth: CGFloat = 1 { public var borderWidth: CGFloat = 1 {
didSet { didSet { layer.borderWidth = borderWidth }
layer.borderWidth = borderWidth
}
} }
/// border color of the Checkbox /// border color of the Checkbox
public var borderColor: UIColor = .mvmBlack { public var borderColor: UIColor = .mvmBlack {
didSet { didSet { layer.borderColor = borderColor.cgColor }
layer.borderColor = borderColor.cgColor
}
} }
/** /**
@ -359,6 +353,7 @@ import MVMCore
} }
override open func accessibilityActivate() -> Bool { override open func accessibilityActivate() -> Bool {
guard isEnabled else { return false }
sendActions(for: .touchUpInside) sendActions(for: .touchUpInside)
return true return true
} }
@ -367,9 +362,7 @@ import MVMCore
// MARK: - Molecular // MARK: - Molecular
//-------------------------------------------------- //--------------------------------------------------
open func needsToBeConstrained() -> Bool { open func needsToBeConstrained() -> Bool { true }
return true
}
open override func reset() { open override func reset() {
super.reset() super.reset()
@ -396,11 +389,9 @@ import MVMCore
} }
private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
if let actionMap = actionModel.toJSON() { var additionalDataToUpdate = additionalData ?? [:]
var additionalDatatoUpdate = additionalData ?? [:] additionalDataToUpdate[KeySourceModel] = checkboxModel
additionalDatatoUpdate[KeySourceModel] = checkboxModel MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataToUpdate, delegateObject: delegateObject)
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject)
}
} }
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {

View File

@ -16,6 +16,7 @@ import Foundation
public static var identifier: String = "checkbox" public static var identifier: String = "checkbox"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var checked: Bool = false public var checked: Bool = false
public var enabled: Bool = true public var enabled: Bool = true
public var animated: Bool = true public var animated: Bool = true
@ -44,6 +45,7 @@ import Foundation
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case accessibilityIdentifier
case checked case checked
case enabled case enabled
case inverted case inverted
@ -69,9 +71,7 @@ import Foundation
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? { checked }
return checked
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer
@ -89,6 +89,8 @@ import Foundation
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) { if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) {
self.borderWidth = borderWidth self.borderWidth = borderWidth
} }
@ -169,6 +171,7 @@ import Foundation
try container.encode(borderWidth, forKey: .borderWidth) try container.encode(borderWidth, forKey: .borderWidth)
try container.encode(checked, forKey: .checked) try container.encode(checked, forKey: .checked)
try container.encode(inverted, forKey: .inverted) try container.encode(inverted, forKey: .inverted)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(checkColor, forKey: .checkColor) try container.encodeIfPresent(checkColor, forKey: .checkColor)
try container.encodeIfPresent(invertedColor, forKey: .invertedColor) try container.encodeIfPresent(invertedColor, forKey: .invertedColor)
try container.encodeIfPresent(invertedBackgroundColor, forKey: .invertedBackgroundColor) try container.encodeIfPresent(invertedBackgroundColor, forKey: .invertedBackgroundColor)

View File

@ -71,6 +71,7 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
addTarget(self, action: #selector(tapAction), for: .touchUpInside) addTarget(self, action: #selector(tapAction), for: .touchUpInside)

View File

@ -6,15 +6,16 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol { open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public static var identifier: String = "heart" public static var identifier: String = "heart"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var isActive: Bool = false public var isActive: Bool = false
public var activeColor: Color = Color(uiColor: .mvmRed) public var activeColor: Color = Color(uiColor: .mvmRed)
public var inActiveColor: Color = Color(uiColor: .clear) public var inActiveColor: Color = Color(uiColor: .clear)
@ -24,9 +25,11 @@ open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case isActive case isActive
case activeColor case activeColor
case inActiveColor case inActiveColor
@ -43,13 +46,18 @@ open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
if let isActive = try typeContainer.decodeIfPresent(Bool.self, forKey: .isActive) { if let isActive = try typeContainer.decodeIfPresent(Bool.self, forKey: .isActive) {
self.isActive = isActive self.isActive = isActive
} }
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
if let activeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .activeColor) { if let activeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .activeColor) {
self.activeColor = activeColor self.activeColor = activeColor
} }
if let inActiveColor = try typeContainer.decodeIfPresent(Color.self, forKey: .inActiveColor) { if let inActiveColor = try typeContainer.decodeIfPresent(Color.self, forKey: .inActiveColor) {
self.inActiveColor = inActiveColor self.inActiveColor = inActiveColor
} }
if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) { if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) {
self.action = action self.action = action
} }
@ -61,6 +69,7 @@ open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(isActive, forKey: .isActive) try container.encode(isActive, forKey: .isActive)
try container.encode(activeColor, forKey: .activeColor) try container.encode(activeColor, forKey: .activeColor)

View File

@ -6,9 +6,12 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
open class RadioBox: Control, MFButtonProtocol { open class RadioBox: Control, MFButtonProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public let label = Label(fontStyle: .RegularBodySmall) public let label = Label(fontStyle: .RegularBodySmall)
public let subTextLabel = Label(fontStyle: .RegularMicro) public let subTextLabel = Label(fontStyle: .RegularMicro)
public var isOutOfStock = false public var isOutOfStock = false
@ -26,22 +29,20 @@ open class RadioBox: Control, MFButtonProtocol {
var additionalData: [AnyHashable: Any]? var additionalData: [AnyHashable: Any]?
public var radioBoxModel: RadioBoxModel? { public var radioBoxModel: RadioBoxModel? {
return model as? RadioBoxModel model as? RadioBoxModel
} }
public override var isSelected: Bool { public override var isSelected: Bool {
didSet { didSet { updateAccessibility() }
updateAccessibility()
}
} }
public override var isEnabled: Bool { public override var isEnabled: Bool {
didSet { didSet { updateAccessibility() }
updateAccessibility()
}
} }
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
//--------------------------------------------------
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
@ -75,8 +76,6 @@ open class RadioBox: Control, MFButtonProtocol {
isAccessibilityElement = true isAccessibilityElement = true
} }
// MARK: - MoleculeViewProtocol
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
guard let model = model as? RadioBoxModel else { return } guard let model = model as? RadioBoxModel else { return }
@ -99,7 +98,9 @@ open class RadioBox: Control, MFButtonProtocol {
accentColor = .mvmRed accentColor = .mvmRed
} }
//--------------------------------------------------
// MARK: - State Handling // MARK: - State Handling
//--------------------------------------------------
open override func draw(_ layer: CALayer, in ctx: CGContext) { open override func draw(_ layer: CALayer, in ctx: CGContext) {
// Draw the strikethrough // Draw the strikethrough
@ -213,21 +214,29 @@ open class RadioBox: Control, MFButtonProtocol {
return mask return mask
} }
//--------------------------------------------------
// MARK: - Accessibility // MARK: - Accessibility
//--------------------------------------------------
public func updateAccessibility() { public func updateAccessibility() {
var message = "" var message = ""
if let labelText = label.text, label.hasText { if let labelText = label.text, label.hasText {
message += labelText + ", " message += labelText + ", "
} }
if let subLabelText = subTextLabel.text, subTextLabel.hasText { if let subLabelText = subTextLabel.text, subTextLabel.hasText {
message += subLabelText + ", " message += subLabelText + ", "
} }
accessibilityLabel = message
accessibilityLabel = message
accessibilityTraits = .button accessibilityTraits = .button
if isSelected { if isSelected {
accessibilityTraits.insert(.selected) accessibilityTraits.insert(.selected)
} }
if !isEnabled { if !isEnabled {
accessibilityTraits.insert(.notEnabled) accessibilityTraits.insert(.notEnabled)
} }

View File

@ -6,10 +6,17 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
open class RadioBoxCollectionViewCell: CollectionViewCell { open class RadioBoxCollectionViewCell: CollectionViewCell {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
public let radioBox = RadioBox() public let radioBox = RadioBox()
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func reset() { open override func reset() {
super.reset() super.reset()
backgroundColor = .clear backgroundColor = .clear

View File

@ -6,12 +6,16 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers public class RadioBoxModel: MoleculeModelProtocol { @objcMembers public class RadioBoxModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "radioBox" public static var identifier: String = "radioBox"
public var text: String public var text: String
public var subText: String? public var subText: String?
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var selectedAccentColor: Color? public var selectedAccentColor: Color?
public var selected: Bool = false public var selected: Bool = false
public var enabled: Bool = true public var enabled: Bool = true
@ -19,12 +23,17 @@ import Foundation
public var fieldValue: String? public var fieldValue: String?
public var action: ActionModelProtocol? public var action: ActionModelProtocol?
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case text case text
case subText case subText
case selectedAccentColor case selectedAccentColor
case backgroundColor case backgroundColor
case accessibilityIdentifier
case selected case selected
case enabled case enabled
case strikethrough case strikethrough
@ -32,18 +41,26 @@ import Foundation
case action case action
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
text = try typeContainer.decode(String.self, forKey: .text) text = try typeContainer.decode(String.self, forKey: .text)
subText = try typeContainer.decodeIfPresent(String.self, forKey: .subText) subText = try typeContainer.decodeIfPresent(String.self, forKey: .subText)
selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor) selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
if let isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { if let isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) {
selected = isSelected selected = isSelected
} }
if let isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { if let isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
enabled = isEnabled enabled = isEnabled
} }
if let isStrikeTrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { if let isStrikeTrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) {
strikethrough = isStrikeTrough strikethrough = isStrikeTrough
} }
@ -59,6 +76,7 @@ import Foundation
try container.encodeIfPresent(subText, forKey: .subText) try container.encodeIfPresent(subText, forKey: .subText)
try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor) try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(selected, forKey: .selected) try container.encode(selected, forKey: .selected)
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)
try container.encode(strikethrough, forKey: .strikethrough) try container.encode(strikethrough, forKey: .strikethrough)

View File

@ -57,11 +57,12 @@ open class RadioBoxes: View {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
self.delegateObject = delegateObject self.delegateObject = delegateObject
guard let radioBoxesModel = model as? RadioBoxesModel else { return } guard let model = model as? RadioBoxesModel else { return }
boxes = radioBoxesModel.boxes boxes = model.boxes
FormValidator.setupValidation(for: radioBoxesModel, delegate: delegateObject?.formHolderDelegate) FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
backgroundColor = radioBoxesModel.backgroundColor?.uiColor backgroundColor = model.backgroundColor?.uiColor
registerCells() registerCells()
setHeight() setHeight()
collectionView.reloadData() collectionView.reloadData()
@ -168,4 +169,3 @@ extension RadioBoxes: UICollectionViewDelegate {
cell.updateAccessibility() cell.updateAccessibility()
} }
} }

View File

@ -6,17 +6,25 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers public class RadioBoxesModel: MoleculeModelProtocol, FormFieldProtocol { @objcMembers public class RadioBoxesModel: MoleculeModelProtocol, FormFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "radioBoxes" public static var identifier: String = "radioBoxes"
public var boxes: [RadioBoxModel] public var boxes: [RadioBoxModel]
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var selectedAccentColor: Color? public var selectedAccentColor: Color?
public var boxesColor: Color? public var boxesColor: Color?
public var fieldKey: String? public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable? public var baseValue: AnyHashable?
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Returns the fieldValue of the selected box, otherwise the text of the selected box. /// Returns the fieldValue of the selected box, otherwise the text of the selected box.
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? {
let selectedBox = boxes.first { (box) -> Bool in let selectedBox = boxes.first { (box) -> Bool in
@ -25,20 +33,30 @@ import Foundation
return selectedBox?.fieldValue ?? selectedBox?.text return selectedBox?.fieldValue ?? selectedBox?.text
} }
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case selectedAccentColor case selectedAccentColor
case backgroundColor case backgroundColor
case accessibilityIdentifier
case boxesColor case boxesColor
case boxes case boxes
case fieldKey case fieldKey
case groupName case groupName
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor) selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
boxesColor = try typeContainer.decodeIfPresent(Color.self, forKey: .boxesColor) boxesColor = try typeContainer.decodeIfPresent(Color.self, forKey: .boxesColor)
boxes = try typeContainer.decode([RadioBoxModel].self, forKey: .boxes) boxes = try typeContainer.decode([RadioBoxModel].self, forKey: .boxes)
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
@ -54,6 +72,7 @@ import Foundation
try container.encode(boxes, forKey: .boxes) try container.encode(boxes, forKey: .boxes)
try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor) try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encode(groupName, forKey: .groupName) try container.encode(groupName, forKey: .groupName)
} }

View File

@ -15,9 +15,7 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
public var diameter: CGFloat = 30 { public var diameter: CGFloat = 30 {
didSet { didSet { widthConstraint?.constant = diameter }
widthConstraint?.constant = diameter
}
} }
@objc public override var isSelected: Bool { @objc public override var isSelected: Bool {
@ -33,12 +31,10 @@ import UIKit
var additionalData: [AnyHashable: Any]? var additionalData: [AnyHashable: Any]?
public var radioModel: RadioButtonModel? { public var radioModel: RadioButtonModel? {
return model as? RadioButtonModel model as? RadioButtonModel
} }
lazy public var radioGroupName: String? = { lazy public var radioGroupName: String? = { radioModel?.fieldKey }()
return radioModel?.fieldKey
}()
lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = { lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = {
@ -95,33 +91,34 @@ import UIKit
if !isEnabled { if !isEnabled {
return return
} }
let wasPreviouslySelected = isSelected let wasPreviouslySelected = isSelected
if let radioButtonModel = radioButtonSelectionHelper { if let radioButtonModel = radioButtonSelectionHelper {
radioButtonModel.selected(self) radioButtonModel.selected(self)
} else { } else {
isSelected = !isSelected isSelected = !isSelected
} }
if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected { if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected {
Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel) Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel)
} }
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
setNeedsDisplay() setNeedsDisplay()
} }
public func isValidField() -> Bool { public func isValidField() -> Bool { isSelected }
return isSelected
}
public func formFieldName() -> String? { public func formFieldName() -> String? {
return radioModel?.fieldKey radioModel?.fieldKey
} }
public func formFieldGroupName() -> String? { public func formFieldGroupName() -> String? {
return radioModel?.fieldKey radioModel?.fieldKey
} }
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? {
return radioModel?.fieldValue radioModel?.fieldValue
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -6,7 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
import MVMCore import MVMCore
@ -17,6 +16,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
public static var identifier: String = "radioButton" public static var identifier: String = "radioButton"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var state: Bool = false public var state: Bool = false
public var enabled: Bool = true public var enabled: Bool = true
@ -35,6 +35,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case state case state
case enabled case enabled
case fieldValue case fieldValue
@ -56,9 +57,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
// MARK: - Validation // MARK: - Validation
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? { fieldValue }
return fieldValue
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Codec // MARK: - Codec
@ -76,6 +75,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
} }
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
baseValue = state baseValue = state
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
@ -89,6 +89,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(state, forKey: .state) try container.encode(state, forKey: .state)
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers public class RadioButtonSelectionHelper: FormFieldProtocol { @objcMembers public class RadioButtonSelectionHelper: FormFieldProtocol {
//-------------------------------------------------- //--------------------------------------------------
@ -77,7 +75,5 @@ import Foundation
// MARK: - FormValidationFormFieldProtocol // MARK: - FormValidationFormFieldProtocol
extension RadioButtonSelectionHelper { extension RadioButtonSelectionHelper {
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? { selectedRadioButtonModel?.fieldValue }
return selectedRadioButtonModel?.fieldValue
}
} }

View File

@ -77,6 +77,7 @@ open class RadioSwatch: Control, MFButtonProtocol {
//------------------------------------------------------ //------------------------------------------------------
// MARK: - State Handling // MARK: - State Handling
//------------------------------------------------------ //------------------------------------------------------
open override func draw(_ layer: CALayer, in ctx: CGContext) { open override func draw(_ layer: CALayer, in ctx: CGContext) {
//Draw the swatch //Draw the swatch
circleLayer?.removeFromSuperlayer() circleLayer?.removeFromSuperlayer()

View File

@ -6,7 +6,7 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
open class RadioSwatchCollectionViewCell: CollectionViewCell { open class RadioSwatchCollectionViewCell: CollectionViewCell {
public let radioSwatch = RadioSwatch() public let radioSwatch = RadioSwatch()

View File

@ -6,11 +6,15 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers public class RadioSwatchModel: MoleculeModelProtocol { @objcMembers public class RadioSwatchModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "radioSwatch" public static var identifier: String = "radioSwatch"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var color: Color = Color(uiColor: .mvmBlue) public var color: Color = Color(uiColor: .mvmBlue)
public var text: String? public var text: String?
public var selected: Bool = false public var selected: Bool = false
@ -19,9 +23,14 @@ import Foundation
public var fieldValue: String? public var fieldValue: String?
public var action: ActionModelProtocol? public var action: ActionModelProtocol?
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case color case color
case text case text
case selected case selected
@ -31,22 +40,33 @@ import Foundation
case action case action
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) { if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) {
self.color = color self.color = color
} }
text = try typeContainer.decodeIfPresent(String.self, forKey: .text) text = try typeContainer.decodeIfPresent(String.self, forKey: .text)
if let selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { if let selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) {
self.selected = selected self.selected = selected
} }
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
self.enabled = enabled self.enabled = enabled
} }
if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) {
self.strikethrough = strikethrough self.strikethrough = strikethrough
} }
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
action = try typeContainer.decodeModelIfPresent(codingKey: .action) action = try typeContainer.decodeModelIfPresent(codingKey: .action)
} }
@ -55,6 +75,7 @@ import Foundation
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(color, forKey: .color) try container.encode(color, forKey: .color)
try container.encodeIfPresent(text, forKey: .text) try container.encodeIfPresent(text, forKey: .text)
try container.encode(selected, forKey: .selected) try container.encode(selected, forKey: .selected)
@ -64,5 +85,3 @@ import Foundation
try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(action, forKey: .action)
} }
} }

View File

@ -55,9 +55,9 @@ open class RadioSwatches: View {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
self.delegateObject = delegateObject self.delegateObject = delegateObject
guard let radioSwatchesModel = model as? RadioSwatchesModel else { return } guard let model = model as? RadioSwatchesModel else { return }
swatches = radioSwatchesModel.swatches swatches = model.swatches
FormValidator.setupValidation(for: radioSwatchesModel, delegate: delegateObject?.formHolderDelegate) FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
collectionView.reloadData() collectionView.reloadData()
} }

View File

@ -6,16 +6,24 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers public class RadioSwatchesModel: MoleculeModelProtocol, FormFieldProtocol { @objcMembers public class RadioSwatchesModel: MoleculeModelProtocol, FormFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "radioSwatches" public static var identifier: String = "radioSwatches"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var swatches: [RadioSwatchModel] public var swatches: [RadioSwatchModel]
public var fieldKey: String? public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable? public var baseValue: AnyHashable?
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Returns the fieldValue of the selected swatch, otherwise the text of selected swatch. /// Returns the fieldValue of the selected swatch, otherwise the text of selected swatch.
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? {
let selectedSwatch = swatches.first { (swatch) -> Bool in let selectedSwatch = swatches.first { (swatch) -> Bool in
@ -24,17 +32,27 @@ import Foundation
return selectedSwatch?.fieldValue ?? selectedSwatch?.text return selectedSwatch?.fieldValue ?? selectedSwatch?.text
} }
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case swatches case swatches
case fieldKey case fieldKey
case groupName case groupName
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
swatches = try typeContainer.decode([RadioSwatchModel].self, forKey: .swatches) swatches = try typeContainer.decode([RadioSwatchModel].self, forKey: .swatches)
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
@ -47,6 +65,7 @@ import Foundation
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(swatches, forKey: .swatches) try container.encode(swatches, forKey: .swatches)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encode(groupName, forKey: .groupName) try container.encode(groupName, forKey: .groupName)

View File

@ -40,7 +40,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
/// Executes logic before state change. If false, then toggle state will not change and the didToggleAction will not execute. /// Executes logic before state change. If false, then toggle state will not change and the didToggleAction will not execute.
public var shouldToggleAction: ActionBlockConfirmation? = { public var shouldToggleAction: ActionBlockConfirmation? = {
return { return true } return { true }
}() }()
// Sizes are from InVision design specs. // Sizes are from InVision design specs.
@ -69,9 +69,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
/// Simple means to prevent user interaction with the toggle. /// Simple means to prevent user interaction with the toggle.
public var isLocked: Bool = false { public var isLocked: Bool = false {
didSet { didSet { isUserInteractionEnabled = !isLocked }
isUserInteractionEnabled = !isLocked
}
} }
/// The state on the toggle. Default value: false. /// The state on the toggle. Default value: false.
@ -109,7 +107,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
} }
public var toggleModel: ToggleModel? { public var toggleModel: ToggleModel? {
return model as? ToggleModel model as? ToggleModel
} }
//-------------------------------------------------- //--------------------------------------------------
@ -392,21 +390,17 @@ public typealias ActionBlockConfirmation = () -> (Bool)
accessibilityLabel = accessibileString accessibilityLabel = accessibileString
} }
let actionMap = model.action?.toJSON()
let alternateActionMap = model.alternateAction?.toJSON()
let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model)
if actionMap != nil || alternateActionMap != nil { if model.action != nil || model.alternateAction != nil {
didToggleAction = { [weak self] in didToggleAction = { [weak self] in
guard let self = self else { return } guard let self = self else { return }
if self.isOn { if self.isOn {
if actionMap != nil { if let action = model.action {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} }
} else { } else {
if alternateActionMap != nil { if let action = model.alternateAction ?? model.action {
MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} else if actionMap != nil {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} }
} }
} }
@ -414,18 +408,14 @@ public typealias ActionBlockConfirmation = () -> (Bool)
} }
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return Self.getContainerHeight() Self.getContainerHeight()
} }
} }
// MARK: - MVMCoreUIViewConstrainingProtocol // MARK: - MVMCoreUIViewConstrainingProtocol
extension Toggle { extension Toggle {
public func needsToBeConstrained() -> Bool { public func needsToBeConstrained() -> Bool { true }
return true
}
public func horizontalAlignment() -> UIStackView.Alignment { public func horizontalAlignment() -> UIStackView.Alignment { .trailing }
return .trailing
}
} }

View File

@ -6,8 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import UIKit
public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol { public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
@ -15,6 +13,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
//-------------------------------------------------- //--------------------------------------------------
public static var identifier: String = "toggle" public static var identifier: String = "toggle"
public var accessibilityIdentifier: String?
public var backgroundColor: Color? public var backgroundColor: Color?
public var state: Bool = false public var state: Bool = false
public var animated: Bool = true public var animated: Bool = true
@ -42,6 +41,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
case enabled case enabled
case action case action
case backgroundColor case backgroundColor
case accessibilityIdentifier
case alternateAction case alternateAction
case accessibilityText case accessibilityText
case onTintColor case onTintColor
@ -56,9 +56,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? { state }
return state
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer
@ -91,6 +89,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
action = try typeContainer.decodeModelIfPresent(codingKey: .action) action = try typeContainer.decodeModelIfPresent(codingKey: .action)
alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
if let onTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onTintColor) { if let onTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onTintColor) {
self.onTintColor = onTintColor self.onTintColor = onTintColor
@ -120,6 +119,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(action, forKey: .action)
try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)

View File

@ -16,6 +16,7 @@ import Foundation
public static var identifier: String = "dashLine" public static var identifier: String = "dashLine"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var dashColor: Color = Color(uiColor: .mvmCoolGray3) public var dashColor: Color = Color(uiColor: .mvmCoolGray3)
public var dashColor_inverted: Color = Color(uiColor: .mvmWhite) public var dashColor_inverted: Color = Color(uiColor: .mvmWhite)
public var isHidden: Bool = false public var isHidden: Bool = false
@ -36,6 +37,7 @@ import Foundation
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case dashColor_inverted case dashColor_inverted
case dashColor case dashColor
case isHidden case isHidden
@ -57,6 +59,7 @@ import Foundation
} }
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
if let isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden) { if let isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden) {
self.isHidden = isHidden self.isHidden = isHidden
@ -69,5 +72,6 @@ import Foundation
try container.encode(dashColor, forKey: .dashColor) try container.encode(dashColor, forKey: .dashColor)
try container.encode(isHidden, forKey: .isHidden) try container.encode(isHidden, forKey: .isHidden)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
} }
} }

View File

@ -38,7 +38,7 @@ public typealias ActionBlock = () -> ()
} }
public var getRange: NSRange { public var getRange: NSRange {
return NSRange(location: 0, length: text?.count ?? 0) NSRange(location: 0, length: text?.count ?? 0)
} }
//------------------------------------------------------ //------------------------------------------------------
@ -209,7 +209,7 @@ public typealias ActionBlock = () -> ()
/// Default /// Default
@objc open class func label() -> Label { @objc open class func label() -> Label {
return Label(frame: .zero) Label(frame: .zero)
} }
//------------------------------------------------------ //------------------------------------------------------
@ -381,9 +381,7 @@ public typealias ActionBlock = () -> ()
} }
case let actionAtt as LabelAttributeActionModel: case let actionAtt as LabelAttributeActionModel:
addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) { addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) {
if let data = try? actionAtt.action.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] { MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionAtt.action, additionalData: additionalData, delegateObject: delegateObject)
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
} }
addActionAttributes(range: range, string: attributedString) addActionAttributes(range: range, string: attributedString)
@ -794,17 +792,11 @@ extension Label {
accessibilityTraits = .staticText accessibilityTraits = .staticText
} }
public func needsToBeConstrained() -> Bool { public func needsToBeConstrained() -> Bool { true }
return true
}
public func horizontalAlignment() -> UIStackView.Alignment { public func horizontalAlignment() -> UIStackView.Alignment { .leading }
return .leading
}
public func copyBackgroundColor() -> Bool { public func copyBackgroundColor() -> Bool { true }
return true
}
} }
// MARK: - Multi-Link Functionality // MARK: - Multi-Link Functionality
@ -916,6 +908,7 @@ extension UITapGestureRecognizer {
if label.makeWholeViewClickable { if label.makeWholeViewClickable {
return true return true
} }
guard let abstractContainer = label.abstractTextContainer() else { return false } guard let abstractContainer = label.abstractTextContainer() else { return false }
let textContainer = abstractContainer.0 let textContainer = abstractContainer.0
let layoutManager = abstractContainer.1 let layoutManager = abstractContainer.1
@ -990,6 +983,7 @@ extension Label {
} }
} }
} }
return false return false
} }
} }

View File

@ -12,9 +12,7 @@ open class LabelAttributeActionModel: LabelAttributeModel {
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
override public class var identifier: String { override public class var identifier: String { "action" }
return "action"
}
var action: ActionModelProtocol var action: ActionModelProtocol

View File

@ -12,9 +12,7 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
override public class var identifier: String { override public class var identifier: String { "color" }
return "color"
}
var textColor: Color? var textColor: Color?

View File

@ -12,9 +12,7 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
override public class var identifier: String { override public class var identifier: String { "font" }
return "font"
}
var style: Styler.Font? var style: Styler.Font?
var name: String? var name: String?

View File

@ -12,9 +12,7 @@ class LabelAttributeImageModel: LabelAttributeModel {
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
override public class var identifier: String { override public class var identifier: String { "image" }
return "image"
}
var size: CGFloat? var size: CGFloat?
var name: String? var name: String?

View File

@ -12,20 +12,14 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public static var categoryName: String { public static var categoryName: String { "\(LabelAttributeModel.self)" }
return "\(LabelAttributeModel.self)"
}
public static var categoryCodingKey: String { public static var categoryCodingKey: String { "type" }
return "type"
}
public class var identifier: String { public class var identifier: String { "" }
return ""
}
var type: String { var type: String {
get { return Self.identifier } get { Self.identifier }
} }
var location: Int var location: Int

View File

@ -12,9 +12,7 @@
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
override public class var identifier: String { override public class var identifier: String { "strikethrough" }
return "strikethrough"
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer

View File

@ -14,9 +14,7 @@ import UIKit
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
override public class var identifier: String { override public class var identifier: String { "underline" }
return "underline"
}
/// This returns the NSUnderlineStyle used in NSAttributedValue. If there is a pattern, it will return /// This returns the NSUnderlineStyle used in NSAttributedValue. If there is a pattern, it will return
/// a new NSUnderlineStyle derived from the bitmask of style | pattern. /// a new NSUnderlineStyle derived from the bitmask of style | pattern.

View File

@ -21,7 +21,8 @@
public var addSizeConstraintsForAspectRatio = true public var addSizeConstraintsForAspectRatio = true
public var shouldNotifyDelegateOnUpdate = true public var shouldNotifyDelegateOnUpdate = true
public var shouldNotifyDelegateOnDefaultSizeChange = false
// Allows for a view to hardcode which height to use if there is none in the json. // Allows for a view to hardcode which height to use if there is none in the json.
var imageWidth: CGFloat? var imageWidth: CGFloat?
var imageHeight: CGFloat? var imageHeight: CGFloat?
@ -228,13 +229,13 @@
let widthWillChange = !MVMCoreGetterUtility.cgfequal(widthConstraint?.constant ?? 0, width ?? 0) let widthWillChange = !MVMCoreGetterUtility.cgfequal(widthConstraint?.constant ?? 0, width ?? 0)
let heightWillChange = !MVMCoreGetterUtility.cgfequal(heightConstraint?.constant ?? 0, height ?? 0) let heightWillChange = !MVMCoreGetterUtility.cgfequal(heightConstraint?.constant ?? 0, height ?? 0)
let sizeWillChange = (width == nil || height == nil) && !(size?.equalTo(imageView.image?.size ?? CGSize.zero) ?? false) let sizeWillChange = shouldNotifyDelegateOnDefaultSizeChange && (width == nil || height == nil) && !(size?.equalTo(imageView.image?.size ?? CGSize.zero) ?? false)
let heightChangeFromSpinner = (heightConstraint?.isActive ?? false) ? false : ((height ?? size?.height) ?? 0) < loadingSpinnerHeightConstraint?.constant ?? CGFloat.leastNormalMagnitude let heightChangeFromSpinner = (heightConstraint?.isActive ?? false) ? false : ((height ?? size?.height) ?? 0) < loadingSpinnerHeightConstraint?.constant ?? CGFloat.leastNormalMagnitude
return widthWillChange || heightWillChange || sizeWillChange || heightChangeFromSpinner return widthWillChange || heightWillChange || sizeWillChange || heightChangeFromSpinner
} }
// Constrains the image view to be the size provided. Used to size it to the image to fix aspect fit defect. // Constrains the image view to be the size provided. Used to size it to the image to fix aspect fit defect.
func addConstraints(width: NSNumber?, height: NSNumber?, size: CGSize?) { func addConstraints(width: CGFloat?, height: CGFloat?, size: CGSize?) {
widthConstraint?.isActive = false widthConstraint?.isActive = false
heightConstraint?.isActive = false heightConstraint?.isActive = false
@ -242,15 +243,15 @@
guard addSizeConstraintsForAspectRatio else { return } guard addSizeConstraintsForAspectRatio else { return }
if let width = width, let height = height { if let width = width, let height = height {
setHeight(height.cgfloat()) setHeight(height)
setWidth(width.cgfloat()) setWidth(width)
} else if let width = width, let size = size { } else if let width = width, let size = size {
setWidth(width.cgfloat()) setWidth(width)
heightConstraint = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: size.height/size.width) heightConstraint = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: size.height/size.width)
heightConstraint?.priority = UILayoutPriority(rawValue: 900) heightConstraint?.priority = UILayoutPriority(rawValue: 900)
heightConstraint?.isActive = true heightConstraint?.isActive = true
} else if let height = height, let size = size { } else if let height = height, let size = size {
setHeight(height.cgfloat()) setHeight(height)
widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: size.width/size.height) widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: size.width/size.height)
widthConstraint?.priority = UILayoutPriority(rawValue: 900) widthConstraint?.priority = UILayoutPriority(rawValue: 900)
widthConstraint?.isActive = true widthConstraint?.isActive = true
@ -288,7 +289,7 @@
if shouldLoadImage(withName: imageModel.image, width: width, height: height) { if shouldLoadImage(withName: imageModel.image, width: width, height: height) {
imageView.image = nil imageView.image = nil
imageView.animatedImage = nil imageView.animatedImage = nil
loadImage(withName: imageModel.image, format: imageModel.imageFormat, width: width as NSNumber?, height: height as NSNumber?, customFallbackImage: imageModel.fallbackImage, localBundle: imageModel.localBundle) loadImage(withName: imageModel.image, format: imageModel.imageFormat, width: width, height: height, customFallbackImage: imageModel.fallbackImage, localBundle: imageModel.localBundle)
} }
if let contentMode = imageModel.contentMode { if let contentMode = imageModel.contentMode {
@ -309,13 +310,13 @@
// MARK: - Load Methods // MARK: - Load Methods
//-------------------------------------------------- //--------------------------------------------------
public func loadImage(withName imageName: String?, format: String? = nil, width: NSNumber? = nil, height: NSNumber? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) { public func loadImage(withName imageName: String?, format: String? = nil, width: CGFloat? = nil, height: CGFloat? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) {
let completionBlock = completionHandler ?? defaultCompletionBlock() let completionBlock = completionHandler ?? defaultCompletionBlock()
MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in
self.currentImageName = imageName self.currentImageName = imageName
self.currentImageWidth = width?.cgfloat() self.currentImageWidth = width
self.currentImageHeight = height?.cgfloat() self.currentImageHeight = height
if MVMCoreCache.isHostedImage(imageName) { if MVMCoreCache.isHostedImage(imageName) {
self.loadingSpinner.resumeSpinnerAfterDelay() self.loadingSpinner.resumeSpinnerAfterDelay()
self.loadingSpinnerHeightConstraint?.constant = self.spinnerHeight self.loadingSpinnerHeightConstraint?.constant = self.spinnerHeight
@ -338,9 +339,9 @@
let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback") let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback")
if let format = format, format.lowercased().contains("gif") { if let format = format, format.lowercased().contains("gif") {
// Gifs aren't supported by default and need special handling // Gifs aren't supported by default and need special handling
MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock)
} else { } else {
MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock)
} }
}) })
} }
@ -409,6 +410,6 @@
} }
public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) { public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) {
loadImage(withName: imageName, format: format, width: width, height: height, customFallbackImage: customFallbackImage, allowServerParameters: false, completionHandler: completionHandler) loadImage(withName: imageName, format: format, width: width?.cgfloat(), height: height?.cgfloat(), customFallbackImage: customFallbackImage, allowServerParameters: false, completionHandler: completionHandler)
} }
} }

View File

@ -17,7 +17,7 @@ import UIKit
private let stack = Stack<StackModel>() private let stack = Stack<StackModel>()
var multiProgressModel: MultiProgressBarModel? { var multiProgressModel: MultiProgressBarModel? {
get { return model as? MultiProgressBarModel } get { model as? MultiProgressBarModel }
} }
var roundedCorners: Bool = false { var roundedCorners: Bool = false {
@ -85,7 +85,7 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
/// Creates the bars /// Creates the bars
open func set(with progressList: Array<SingleProgressBarModel>, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open func set(with progressList: [SingleProgressBarModel], _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
stack.removeAllItemViews() stack.removeAllItemViews()
guard let stackModel = stack.stackModel else { return } guard let stackModel = stack.stackModel else { return }
@ -103,7 +103,6 @@ import UIKit
stack.set(with: stackModel, delegateObject, additionalData) stack.set(with: stackModel, delegateObject, additionalData)
} }
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
@ -115,6 +114,6 @@ import UIKit
} }
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return (model as? MultiProgressBarModel)?.thickness ?? 8 (model as? MultiProgressBarModel)?.thickness ?? 8
} }
} }

View File

@ -0,0 +1,108 @@
//
// Video.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/26/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import AVKit
open class Video: View {
public let videoViewController = AVPlayerViewController()
private weak var containingView: UIView?
/// Used to track the state and respond..
private var stateKVOToken: NSKeyValueObservation?
open override func setupView() {
super.setupView()
videoViewController.view.translatesAutoresizingMaskIntoConstraints = false
addSubview(videoViewController.view)
NSLayoutConstraint.constraintPinSubview(toSuperview: videoViewController.view)
videoViewController.videoGravity = .resizeAspectFill
}
/// Checks if the video is visible in the molecule delegate
open func isVisibleInDelegate() -> Bool {
guard let containingView = containingView else { return true }
return isVisible(in: containingView)
}
/// Checks if the video is visible in the passed in view
open func isVisible(in view: UIView) -> Bool {
return MVMCoreUIUtility.isView(self, visibleIn: view)
}
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
/// Detach the view from it's previous model before setting.
(self.model as? VideoModel)?.view = nil
containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view
super.set(with: model, delegateObject, additionalData)
guard let model = model as? VideoModel else { return }
if let controller = delegateObject?.moleculeDelegate as? UIViewController {
controller.addChild(videoViewController)
videoViewController.didMove(toParent: controller)
}
videoViewController.showsPlaybackControls = model.showControls
videoViewController.player = model.videoDataManager.player
addStateObserver()
model.addVisibilityHalting(for: self, delegateObject: delegateObject)
switch (model.videoDataManager.videoState) {
case .none:
// Begin loading the video
model.videoDataManager.loadVideo()
case .loaded:
guard isVisibleInDelegate() else { return }
// Video loaded, unhalt it if necessary.
model.halted = false
default:
break
}
}
/// Listens and responds to video loading state changes.
private func addStateObserver() {
removeStateObserver()
guard stateKVOToken == nil,
let model = model as? VideoModel else { return }
// To know when the video player item is done loading.
stateKVOToken =
model.videoDataManager.observe(\.videoState) { [weak self] (item, change) in
guard let self = self,
let model = self.model as? VideoModel,
item == model.videoDataManager else { return }
switch item.videoState {
case .loaded:
// Setting videoController's player must be in the main thread
MVMCoreDispatchUtility.performSyncBlock(onMainThread: {
// Play the video
self.videoViewController.player = item.player
if !model.halted && model.autoPlay && self.isVisibleInDelegate() {
item.player?.play()
UIAccessibility.post(notification: .screenChanged, argument: self)
}
})
case .failed:
if let errorObject = item.loadFailedError {
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
}
default:
break
}
}
}
private func removeStateObserver() {
stateKVOToken?.invalidate()
stateKVOToken = nil
}
deinit {
removeStateObserver()
}
}

View File

@ -0,0 +1,141 @@
//
// VideoDataManager.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/26/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
import AVFoundation
@objcMembers open class VideoDataManager: NSObject {
/// The state of the video.
@objc public enum VideoState: Int {
case none
case loading
case loaded
case failed
}
public let videoURLString: String
public var player: AVPlayer?
// Thread Safe video state handling.
private var _videoState = VideoState.none
private let videoStatueQueue = DispatchQueue(label: "com.vzw.mvmcoreui.videoDataManager.state", attributes: .concurrent)
/// The state of the video. Use KVO to listen for state changes.
@objc public var videoState: VideoState {
get {
var state = VideoState.none
videoStatueQueue.sync {
state = _videoState
}
return state
}
set {
willChangeValue(for: \.videoState)
videoStatueQueue.async(flags: .barrier) {
self._videoState = newValue
}
didChangeValue(for: \.videoState)
}
}
/// Set when the state is set to failed. Follows the same pattern as apple's AVPlayerItem
public var loadFailedError: MVMCoreErrorObject?
private var kvoToken: NSKeyValueObservation?
private var memoryWarningListener: Any?
public init(with videoURLString: String) {
self.videoURLString = videoURLString
super.init()
self.addMemoryWarningListener()
}
public func loadVideo() {
guard videoState != .loading else { return }
removeVideoObserver()
player = nil
videoState = .loading
//Asset loading needs time, calling async method. by tracking asset's propety "duration", if we get the value of duration, we can treat asset load successfully.
let tracksKey = "duration"
MVMCoreCache.shared()?.playerAsset(fromFileName: videoURLString, trackKeys: [tracksKey], onComplete: { [weak self] (asset, fileName, errorObject) in
guard let asset = asset else {
self?.loadFailedError = errorObject
self?.videoState = .failed
return
}
var error: NSError? = nil
let tracksStatus = asset.statusOfValue(forKey: tracksKey, error: &error)
switch tracksStatus {
case .loaded:
//When Assets load successfully, we create playerItem and add playerItem into AVPlayer
self?.player = AVPlayer(playerItem: AVPlayerItem(asset: asset))
self?.addObserverToPlayerItem()
case .failed:
//Asset load fail
//Since checking asset status here, no need to check player.currenItem.asset's media tracks when play button is clicked anymore.
if let error = error,
let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: (asset as? AVURLAsset)?.url.absoluteString ?? self?.videoURLString) {
self?.loadFailedError = errorObject
}
self?.videoState = .failed
default:
break
}
})
}
private func addObserverToPlayerItem() {
removeVideoObserver()
// To know when the video player item is done loading.
guard kvoToken == nil else { return }
kvoToken = player?.currentItem?.observe(\.status) { [weak self] (item, change) in
guard item == self?.player?.currentItem else { return }
switch item.status {
case .readyToPlay:
self?.videoState = .loaded
case .failed:
if let error = item.error,
let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: (item.asset as? AVURLAsset)?.url.absoluteString ?? self?.videoURLString) {
self?.loadFailedError = errorObject
}
self?.videoState = .failed
default:
break
}
}
}
private func removeVideoObserver() {
kvoToken?.invalidate()
kvoToken = nil
}
private func addMemoryWarningListener() {
memoryWarningListener = NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in
self?.removeVideoObserver()
self?.player = nil
self?.videoState = .none
}
}
private func removeMemoryWarningListener() {
guard let observer = memoryWarningListener else { return }
NotificationCenter.default.removeObserver(observer)
memoryWarningListener = nil
}
deinit {
removeVideoObserver()
removeMemoryWarningListener()
}
}

View File

@ -0,0 +1,167 @@
//
// VideoModel.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/26/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
open class VideoModel: MoleculeModelProtocol {
public static var identifier = "video"
public var backgroundColor: Color?
public var video: String
public var showControls = false
public var autoPlay = true
public var alwaysReset = false
weak public var view: Video?
/// When the video is halted because it is no longer visible
public var halted: Bool = false {
didSet {
guard halted != oldValue,
videoDataManager.videoState == .loaded else { return }
if halted {
videoDataManager.player?.pause()
} else {
if alwaysReset {
// Always start video at the beginning.
videoDataManager.player?.seek(to: .zero)
}
if autoPlay {
videoDataManager.player?.play()
}
}
}
}
/// Keeps a reference to the video data.
public var videoDataManager: VideoDataManager
private weak var visibleBehavior: PageVisibilityClosureBehavior?
private weak var scrollBehavior: PageScrolledClosureBehavior?
private var activeListener: Any?
private var resignActiveListener: Any?
private enum CodingKeys: String, CodingKey {
case moleculeName
case video
case showControls
case autoPlay
case alwaysReset
}
public init(_ video: String) {
self.video = video
videoDataManager = VideoDataManager(with: video)
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
video = try typeContainer.decode(String.self, forKey:.video)
if let showControls = try typeContainer.decodeIfPresent(Bool.self, forKey: .showControls) {
self.showControls = showControls
}
if let autoPlay = try typeContainer.decodeIfPresent(Bool.self, forKey: .autoPlay) {
self.autoPlay = autoPlay
}
if let alwaysReset = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysReset) {
self.alwaysReset = alwaysReset
}
videoDataManager = VideoDataManager(with: video)
}
open func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(video, forKey: .video)
try container.encode(showControls, forKey: .showControls)
try container.encode(autoPlay, forKey: .autoPlay)
try container.encode(alwaysReset, forKey: .alwaysReset)
}
open func addVisibilityHalting(for view: Video, delegateObject: MVMCoreUIDelegateObject?) {
self.view = view
halted = false
addVisibleBehavior(for: view, delegateObject: delegateObject)
addScrollBehavior(for: view, delegateObject: delegateObject)
addActiveListener(for: view, delegateObject: delegateObject)
}
/// Adds a behavior to pause the video on page hidden behavior and unpause if necessary on page shown.
open func addVisibleBehavior(for view: Video, delegateObject: MVMCoreUIDelegateObject?) {
let onShow = { [weak self] in
guard let self = self,
let view = self.view,
view.isVisibleInDelegate() else { return }
self.halted = false
}
let onHide: () -> Void = { [weak self] in
self?.halted = true
}
guard visibleBehavior == nil else {
visibleBehavior?.pageShownHandler = onShow
visibleBehavior?.pageHiddenHandler = onHide
return
}
guard var delegate = delegateObject?.behaviorTemplateDelegate else { return }
let pauseBehavior = PageVisibilityClosureBehavior(with: onShow, onPageHiddenHandler: onHide)
delegate.add(behavior: pauseBehavior)
self.visibleBehavior = pauseBehavior
}
/// Adds a behavior to pause the video if scrolled off of the page and unpause if necessary if scrolled on.
open func addScrollBehavior(for view: Video, delegateObject: MVMCoreUIDelegateObject?) {
let onScroll = { [weak self] (scrollView: UIScrollView) in
// If visible to not visible, pause video.
// If not visible to visible, unpause if needed, add visible behavior
guard let self = self,
let view = self.view else { return }
self.halted = !view.isVisible(in: scrollView)
}
guard scrollBehavior == nil else {
scrollBehavior?.pageScrolledHandler = onScroll
return
}
guard var delegate = delegateObject?.behaviorTemplateDelegate else { return }
let scrollBehavior = PageScrolledClosureBehavior(with: onScroll)
delegate.add(behavior: scrollBehavior)
self.scrollBehavior = scrollBehavior
}
open func addActiveListener(for view: Video, delegateObject: MVMCoreUIDelegateObject?) {
removeActiveListener()
resignActiveListener = NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in
self?.halted = true
}
activeListener = NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in
guard let self = self,
let view = self.view,
view.isVisibleInDelegate() else { return }
self.halted = false
}
}
private func removeActiveListener() {
if let observer = activeListener {
NotificationCenter.default.removeObserver(observer)
activeListener = nil
}
if let observer = resignActiveListener {
NotificationCenter.default.removeObserver(observer)
resignActiveListener = nil
}
}
deinit {
removeActiveListener()
}
}

View File

@ -79,6 +79,7 @@ import Foundation
MoleculeObjectMapping.shared()?.register(viewClass: DigitEntryField.self, viewModelClass: DigitEntryFieldModel.self) MoleculeObjectMapping.shared()?.register(viewClass: DigitEntryField.self, viewModelClass: DigitEntryFieldModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: ItemDropdownEntryField.self, viewModelClass: ItemDropdownEntryFieldModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ItemDropdownEntryField.self, viewModelClass: ItemDropdownEntryFieldModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: DateDropdownEntryField.self, viewModelClass: DateDropdownEntryFieldModel.self) MoleculeObjectMapping.shared()?.register(viewClass: DateDropdownEntryField.self, viewModelClass: DateDropdownEntryFieldModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: MultiItemDropdownEntryField.self, viewModelClass: MultiItemDropdownEntryFieldModel.self)
// MARK:- Selectors // MARK:- Selectors
MoleculeObjectMapping.shared()?.register(viewClass: RadioButton.self, viewModelClass: RadioButtonModel.self) MoleculeObjectMapping.shared()?.register(viewClass: RadioButton.self, viewModelClass: RadioButtonModel.self)
@ -106,6 +107,7 @@ import Foundation
MoleculeObjectMapping.shared()?.register(viewClass: RadioButtonLabel.self, viewModelClass: RadioButtonLabelModel.self) MoleculeObjectMapping.shared()?.register(viewClass: RadioButtonLabel.self, viewModelClass: RadioButtonLabelModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: WebView.self, viewModelClass: WebViewModel.self) MoleculeObjectMapping.shared()?.register(viewClass: WebView.self, viewModelClass: WebViewModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: LoadingSpinner.self, viewModelClass: LoadingSpinnerModel.self) MoleculeObjectMapping.shared()?.register(viewClass: LoadingSpinner.self, viewModelClass: LoadingSpinnerModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: Video.self, viewModelClass: VideoModel.self)
// MARK:- Horizontal Combination Molecules // MARK:- Horizontal Combination Molecules
MoleculeObjectMapping.shared()?.register(viewClass: StringAndMoleculeView.self, viewModelClass: StringAndMoleculeModel.self) MoleculeObjectMapping.shared()?.register(viewClass: StringAndMoleculeView.self, viewModelClass: StringAndMoleculeModel.self)
@ -151,6 +153,7 @@ import Foundation
MoleculeObjectMapping.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: ModuleMolecule.self, viewModelClass: ModuleMoleculeModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ModuleMolecule.self, viewModelClass: ModuleMoleculeModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: BGImageMolecule.self, viewModelClass: BGImageMoleculeModel.self) MoleculeObjectMapping.shared()?.register(viewClass: BGImageMolecule.self, viewModelClass: BGImageMoleculeModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: BGVideoImageMolecule.self, viewModelClass: BGVideoImageMoleculeModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionHeader.self, viewModelClass: MoleculeSectionHeaderModel.self) MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionHeader.self, viewModelClass: MoleculeSectionHeaderModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionFooter.self, viewModelClass: MoleculeSectionFooterModel.self) MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionFooter.self, viewModelClass: MoleculeSectionFooterModel.self)
@ -259,6 +262,7 @@ import Foundation
try? ModelRegistry.register(ActionTopAlertModel.self) try? ModelRegistry.register(ActionTopAlertModel.self)
try? ModelRegistry.register(ActionCollapseNotificationModel.self) try? ModelRegistry.register(ActionCollapseNotificationModel.self)
try? ModelRegistry.register(ActionOpenPanelModel.self) try? ModelRegistry.register(ActionOpenPanelModel.self)
try? ModelRegistry.register(ActionTopNotificationModel.self)
// MARK:- Behaviors // MARK:- Behaviors
try? ModelRegistry.register(ScreenBrightnessModifierBehavior.self) try? ModelRegistry.register(ScreenBrightnessModifierBehavior.self)

View File

@ -11,7 +11,7 @@
// MARK: - Outlets // MARK: - Outlets
//-------------------------------------------------- //--------------------------------------------------
public let leftImage = LoadImageView() public let leftImage = LoadImageView(pinnedEdges: .all)
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink() public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
public let rightLabel = Label(fontStyle: .RegularBodySmall) public let rightLabel = Label(fontStyle: .RegularBodySmall)

View File

@ -12,7 +12,7 @@
// MARK: - Outlets // MARK: - Outlets
//-------------------------------------------------- //--------------------------------------------------
public let leftImage = LoadImageView() public let leftImage = LoadImageView(pinnedEdges: .all)
public let headlineBody = HeadlineBody() public let headlineBody = HeadlineBody()
public let rightLabel = Label(fontStyle: .RegularBodySmall) public let rightLabel = Label(fontStyle: .RegularBodySmall)
public let rightLabelStackItem: StackItem public let rightLabelStackItem: StackItem

View File

@ -21,7 +21,8 @@
//-------------------------------------------------- //--------------------------------------------------
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
stack = Stack<StackModel>.createStack(with: [(view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .top)), (view: rightLabel, model: StackItemModel(horizontalAlignment:.fill))], axis: .horizontal) // Fill for left vertical alignment because bottom constraint was breaking with leading. CXTDT-145456
stack = Stack<StackModel>.createStack(with: [(view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .fill)), (view: rightLabel, model: StackItemModel(horizontalAlignment:.fill, verticalAlignment: .leading))], axis: .horizontal)
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
} }
@ -47,7 +48,7 @@
if let heroCenter = heroCenter, if let heroCenter = heroCenter,
let stackItem = stack.stackItems.last as? StackItem { let stackItem = stack.stackItems.last as? StackItem {
let convertedPoint = stack.convert(heroCenter, from: self) let convertedPoint = stack.convert(heroCenter, from: self)
stackItem.containerHelper.alignCenterVerticalConstraint?.constant = convertedPoint.y - stack.bounds.midY stackItem.containerHelper.topConstraint?.constant = max(convertedPoint.y - rightLabel.bounds.midY, 0.0)
} }
return heroCenter return heroCenter
@ -61,7 +62,6 @@
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
guard let model = model as? ListRightVariableRightCaretAllTextAndLinksModel else { return } guard let model = model as? ListRightVariableRightCaretAllTextAndLinksModel else { return }
rightLabel.set(with: model.rightLabel, delegateObject, additionalData) rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
updateAccessibilityLabel() updateAccessibilityLabel()

View File

@ -6,7 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
open class HeaderView: Container { open class HeaderView: Container {
public let line = Line() public let line = Line()
@ -15,7 +14,7 @@ open class HeaderView: Container {
open var molecule: MoleculeViewProtocol? open var molecule: MoleculeViewProtocol?
var headerModel: HeaderModel? { var headerModel: HeaderModel? {
get { return model as? HeaderModel } get { model as? HeaderModel }
} }
/// Convenience function to add a molecule to the view. /// Convenience function to add a molecule to the view.
@ -64,6 +63,6 @@ open class HeaderView: Container {
} }
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing
} }
} }

View File

@ -6,7 +6,6 @@
// Copyright © 2019 Suresh, Kamlesh. All rights reserved. // Copyright © 2019 Suresh, Kamlesh. All rights reserved.
// //
import Foundation
@objcMembers public class MoleculeHeaderModel: HeaderModel, MoleculeModelProtocol, MoleculeContainerModelProtocol { @objcMembers public class MoleculeHeaderModel: HeaderModel, MoleculeModelProtocol, MoleculeContainerModelProtocol {
public static var identifier: String = "header" public static var identifier: String = "header"

View File

@ -17,7 +17,7 @@ public class MoleculeHeaderView: MoleculeContainer {
var line = Line() var line = Line()
var headerModel: MoleculeHeaderModel? { var headerModel: MoleculeHeaderModel? {
get { return model as? MoleculeHeaderModel } get { model as? MoleculeHeaderModel }
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -6,7 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate { @objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {
@ -15,9 +14,7 @@ import Foundation
public let line = Line() public let line = Line()
required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let model = model as? TabBarModel else { guard let model = model as? TabBarModel else { fatalError("model is not TabBarModel") }
fatalError("model is not TabBarModel")
}
self.model = model self.model = model
super.init(frame: .zero) super.init(frame: .zero)
@ -32,7 +29,7 @@ import Foundation
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let model = model as? TabBarModel else { return } guard let model = model as? TabBarModel else { return }
self.model = model self.model = model
@ -57,8 +54,12 @@ import Foundation
var tabs: [UITabBarItem] = [] var tabs: [UITabBarItem] = []
for (index, tab) in model.tabs.enumerated() { for (index, tab) in model.tabs.enumerated() {
let tabBarItem = UITabBarItem(title: tab.title, image: MVMCoreCache.shared()?.getImageFromRegisteredBundles(tab.image), tag: index) let tabBarItem = UITabBarItem(title: tab.title, image: MVMCoreCache.shared()?.getImageFromRegisteredBundles(tab.image), tag: index)
tabBarItem.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -3) tabBarItem.accessibilityLabel = tab.accessibilityText
tabBarItem.setTitleTextAttributes([NSAttributedString.Key.font: MFFonts.mfFontTXRegular(8)], for: .normal) if #available(iOS 13.0, *) {
} else {
tabBarItem.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -3)
tabBarItem.setTitleTextAttributes([NSAttributedString.Key.font: MFFonts.mfFontTXRegular(8)], for: .normal)
}
tabs.append(tabBarItem) tabs.append(tabBarItem)
} }
setItems(tabs, animated: false) setItems(tabs, animated: false)
@ -100,10 +101,7 @@ import Foundation
}) })
} }
public func currentTabIndex() -> Int { public func currentTabIndex() -> Int { model.selectedTab }
return model.selectedTab
}
} }
extension UITabBarItem: MFButtonProtocol { extension UITabBarItem: MFButtonProtocol { }
}

View File

@ -60,17 +60,19 @@ public class TabBarModel: MoleculeModelProtocol {
} }
public class TabBarItemModel: Codable { public class TabBarItemModel: Codable {
var title: String var title: String?
var image: String var image: String
var action: ActionModelProtocol var action: ActionModelProtocol
var accessibilityText: String?
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case title case title
case image case image
case action case action
case accessibilityText
} }
public init(with title: String, image: String, action: ActionModelProtocol) { public init(with title: String?, image: String, action: ActionModelProtocol) {
self.title = title self.title = title
self.image = image self.image = image
self.action = action self.action = action
@ -78,15 +80,17 @@ public class TabBarItemModel: Codable {
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
title = try typeContainer.decode(String.self, forKey: .title) title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
image = try typeContainer.decode(String.self, forKey: .image) image = try typeContainer.decode(String.self, forKey: .image)
action = try typeContainer.decodeModel(codingKey: .action) action = try typeContainer.decodeModel(codingKey: .action)
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(title, forKey: .title) try container.encodeIfPresent(title, forKey: .title)
try container.encode(image, forKey: .image) try container.encode(image, forKey: .image)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
} }
} }

View File

@ -9,12 +9,12 @@
import Foundation import Foundation
@objcMembers public class CarouselItemModel: MoleculeCollectionItemModel, CarouselItemModelProtocol { @objcMembers open class CarouselItemModel: MoleculeCollectionItemModel, CarouselItemModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { open override class var identifier: String {
return "carouselItem" return "carouselItem"
} }
@ -44,7 +44,7 @@ import Foundation
try super.init(from: decoder) try super.init(from: decoder)
} }
public override func encode(to encoder: Encoder) throws { open override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(peakingUI, forKey: .peakingUI) try container.encodeIfPresent(peakingUI, forKey: .peakingUI)

View File

@ -9,7 +9,7 @@
import Foundation import Foundation
/// A model for a collection item that is a container for any molecule. /// A model for a collection item that is a container for any molecule.
@objcMembers public class MoleculeCollectionItemModel: MoleculeContainerModel, CollectionItemModelProtocol { @objcMembers open class MoleculeCollectionItemModel: MoleculeContainerModel, CollectionItemModelProtocol {
open override class var identifier: String { open override class var identifier: String {
return "collectionItem" return "collectionItem"
} }

View File

@ -6,17 +6,15 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers open class ImageBarButtonItem: BarButtonItem { @objcMembers open class ImageBarButtonItem: BarButtonItem {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
public static func create(with image: UIImage?) -> Self { public static func create(with image: UIImage?) -> Self {
let actionObject = ActionDelegate() let actionObject = ActionDelegate()
let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock(_:))) let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock))
button.actionDelegate = actionObject button.actionDelegate = actionObject
return button return button
} }
@ -29,7 +27,7 @@ import Foundation
} }
/// Creates the item with the passed in action map. /// Creates the item with the passed in action map.
public static func create(with image: UIImage?, actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { public static func create(with image: UIImage?, actionMap: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self {
let button = create(with: image) let button = create(with: image)
button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData) button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData)
return button return button
@ -42,4 +40,3 @@ import Foundation
return button return button
} }
} }

View File

@ -6,10 +6,8 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers open class LabelBarButtonItem: BarButtonItem { @objcMembers open class LabelBarButtonItem: BarButtonItem {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------

View File

@ -5,30 +5,47 @@
// Created by Scott Pfeil on 5/18/20. // Created by Scott Pfeil on 5/18/20.
// //
import Foundation
public class NavigationImageButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol { public class NavigationImageButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public static var identifier: String = "navigationImageButton" public static var identifier: String = "navigationImageButton"
public var image: String public var image: String
public var action: ActionModelProtocol public var action: ActionModelProtocol
public var accessibilityText: String? public var accessibilityText: String?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(with image: String, action: ActionModelProtocol) { public init(with image: String, action: ActionModelProtocol) {
self.image = image self.image = image
self.action = action self.action = action
} }
//--------------------------------------------------
// MARK: - Coding Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case image case image
case action case action
case accessibilityIdentifier
case moleculeName case moleculeName
case accessibilityText case accessibilityText
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
image = try typeContainer.decode(String.self, forKey: .image) image = try typeContainer.decode(String.self, forKey: .image)
action = try typeContainer.decodeModel(codingKey: .action) action = try typeContainer.decodeModel(codingKey: .action)
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
@ -37,19 +54,26 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
open func encode(to encoder: Encoder) throws { open func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(image, forKey: .image) try container.encode(image, forKey: .image)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
} }
//--------------------------------------------------
// MARK: - Method
//--------------------------------------------------
/// Convenience function that creates a BarButtonItem for the model. /// Convenience function that creates a BarButtonItem for the model.
public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem {
let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image) let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image)
let navigationImageButton = ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) let buttonItem = ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData)
buttonItem.accessibilityIdentifier = accessibilityIdentifier ?? image
if let accessibilityString = accessibilityText { if let accessibilityString = accessibilityText {
navigationImageButton.accessibilityLabel = accessibilityString buttonItem.accessibilityLabel = accessibilityString
navigationImageButton.isAccessibilityElement = true buttonItem.isAccessibilityElement = true
} }
return navigationImageButton
return buttonItem
} }
} }

View File

@ -6,28 +6,45 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
public class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol { public class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var backgroundColor: Color? public var backgroundColor: Color?
public static var identifier: String = "navigationLabelButton" public static var identifier: String = "navigationLabelButton"
public var accessibilityIdentifier: String?
public var title: String public var title: String
public var action: ActionModelProtocol public var action: ActionModelProtocol
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(with title: String, action: ActionModelProtocol) { public init(with title: String, action: ActionModelProtocol) {
self.title = title self.title = title
self.action = action self.action = action
} }
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case accessibilityIdentifier
case title case title
case action case action
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
title = try typeContainer.decode(String.self, forKey: .title) title = try typeContainer.decode(String.self, forKey: .title)
action = try typeContainer.decodeModel(codingKey: .action) action = try typeContainer.decodeModel(codingKey: .action)
} }
@ -35,10 +52,15 @@ public class NavigationLabelButtonModel: NavigationButtonModelProtocol, Molecule
open func encode(to encoder: Encoder) throws { open func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(title, forKey: .title) try container.encode(title, forKey: .title)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
} }
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Convenience function that creates a BarButtonItem for the model. /// Convenience function that creates a BarButtonItem for the model.
public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem {
return LabelBarButtonItem.create(with: title, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) return LabelBarButtonItem.create(with: title, actionModel: action, delegateObject: delegateObject, additionalData: additionalData)

View File

@ -6,12 +6,13 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol {
open class var identifier: String { //--------------------------------------------------
return "navigationBar" // MARK: - Properties
} //--------------------------------------------------
open class var identifier: String { "navigationBar" }
open var title: String? open var title: String?
open var hidden: Bool open var hidden: Bool
@ -28,13 +29,21 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc
open var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? open var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]?
open var titleView: MoleculeModelProtocol? open var titleView: MoleculeModelProtocol?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init() { public init() {
hidden = false hidden = false
backgroundColor = Color(uiColor: .white) backgroundColor = Color(uiColor: .mvmWhite)
tintColor = Color(uiColor: .black) tintColor = Color(uiColor: .mvmBlack)
line = LineModel(type: .standard) line = LineModel(type: .standard)
} }
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case title case title
@ -48,13 +57,17 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc
case additionalRightButtons case additionalRightButtons
case titleView case titleView
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
title = try typeContainer.decodeIfPresent(String.self, forKey: .title) title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .mvmWhite)
tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .mvmBlack)
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard)
alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton)
backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton) backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton)

View File

@ -27,6 +27,12 @@ open class BGImageMoleculeModel: MoleculeContainerModel {
if bottomPadding == nil { if bottomPadding == nil {
bottomPadding = PaddingDefaultVerticalSpacing3 bottomPadding = PaddingDefaultVerticalSpacing3
} }
if image.contentMode == nil {
image.contentMode = .scaleAspectFill
}
if image.imageFormat == nil {
image.imageFormat = "jpg"
}
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -0,0 +1,98 @@
//
// BGVideoImageMolecule.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/26/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import UIKit
open class BGVideoImageMolecule: BGImageMolecule {
public let video = Video()
/// Used to hide video after loaded.
private var stateKVOToken: NSKeyValueObservation?
private var endObserver: NSObjectProtocol?
open override func setupView() {
super.setupView()
insertSubview(video, aboveSubview: image)
NSLayoutConstraint.constraintPinSubview(toSuperview: video)
}
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? BGVideoImageMoleculeModel else { return }
video.set(with: model.video, delegateObject, additionalData)
video.isHidden = shouldVideoBeHidden()
addStateObserver()
}
open func shouldVideoBeHidden() -> Bool {
guard let model = model as? BGVideoImageMoleculeModel,
model.video.videoDataManager.videoState != .failed else {
return true
}
guard model.video.videoDataManager.videoState == .loaded,
let player = model.video.videoDataManager.player,
let item = player.currentItem,
item.currentTime() == item.duration else {
return false
}
return true
}
/// Listens and responds to video loading state changes to add the end observer.
private func addStateObserver() {
removeStateObserver()
guard stateKVOToken == nil,
let model = model as? BGVideoImageMoleculeModel else { return }
// To know when the video player item is done loading.
stateKVOToken =
model.video.videoDataManager.observe(\.videoState) { [weak self] (item, change) in
guard let self = self,
let model = self.model as? BGVideoImageMoleculeModel,
item == model.video.videoDataManager else { return }
switch item.videoState {
case .loaded:
self.addEndObserver()
case .failed:
MVMCoreDispatchUtility.performBlock(onMainThread: {
self.video.isHidden = true
})
default:
break
}
}
}
private func removeStateObserver() {
stateKVOToken?.invalidate()
stateKVOToken = nil
}
private func addEndObserver() {
removeStateObserver()
guard let model = model as? BGVideoImageMoleculeModel,
let item = model.video.videoDataManager.player?.currentItem else { return }
endObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item, queue: OperationQueue.main) { [weak self] (notification) in
self?.video.isHidden = true
}
}
private func removeEndObserver() {
guard let endObserver = endObserver else { return }
NotificationCenter.default.removeObserver(endObserver)
self.endObserver = nil
}
deinit {
removeStateObserver()
removeEndObserver()
}
}

View File

@ -0,0 +1,38 @@
//
// BGVideoImageMoleculeModel.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/26/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import UIKit
open class BGVideoImageMoleculeModel: BGImageMoleculeModel {
open override class var identifier: String {
return "bgVideoImageContainer"
}
public var video: VideoModel
private enum CodingKeys: String, CodingKey {
case video
}
public init(_ video: VideoModel, image: ImageViewModel, molecule: MoleculeModelProtocol) {
self.video = video
super.init(image, molecule: molecule)
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
video = try typeContainer.decode(VideoModel.self, forKey:.video)
try super.init(from: decoder)
}
open override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(video, forKey: .video)
}
}

View File

@ -6,11 +6,10 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol { extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol {
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
defaultSetup() defaultSetup()
guard let model = model as? CollapsableNotificationModel else { return } guard let model = model as? CollapsableNotificationModel else { return }
backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen
@ -30,6 +29,10 @@ extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol {
MVMCoreUITopAlertBaseView.addAction(to: button, actionMap: topActionMap, additionalData: nil) MVMCoreUITopAlertBaseView.addAction(to: button, actionMap: topActionMap, additionalData: nil)
shortView?.label?.accessibilityTraits = .button shortView?.label?.accessibilityTraits = .button
} }
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
} }
} }

View File

@ -6,17 +6,24 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
extension MVMCoreUITopAlertMainView: MoleculeViewProtocol { extension MVMCoreUITopAlertMainView: MoleculeViewProtocol {
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
defaultSetup() defaultSetup()
guard let model = model as? NotificationModel else { return } guard let model = model as? NotificationModel else { return }
backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen
var actionMap = model.button?.action.toJSON() var actionMap = model.button?.action.toJSON()
if let title = model.button?.title { if let title = model.button?.title {
actionMap?.updateValue(title, forKey: KeyTitle) actionMap?.updateValue(title, forKey: KeyTitle)
} }
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate) 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) setup(withMessage: model.headline.text, subMessage: model.body?.text, color: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil)
} }

View File

@ -6,52 +6,72 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
open class NotificationModel: MoleculeModelProtocol { open class NotificationModel: MoleculeModelProtocol {
public class var identifier: String { //--------------------------------------------------
return "notification" // MARK: - Properties
} //--------------------------------------------------
public class var identifier: String { "notification" }
public var accessibilityIdentifier: String?
public var backgroundColor: Color? public var backgroundColor: Color?
public var headline: LabelModel public var headline: LabelModel
public var body: LabelModel? public var body: LabelModel?
public var button: ButtonModel? public var button: ButtonModel?
public var closeButton: NotificationXButtonModel? public var closeButton: NotificationXButtonModel?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(with headline: LabelModel) { public init(with headline: LabelModel) {
self.headline = headline self.headline = headline
} }
//--------------------------------------------------
// MARK: - Default
//--------------------------------------------------
open func setDefault() { open func setDefault() {
if backgroundColor == nil { if backgroundColor == nil {
backgroundColor = Color(uiColor: .mvmGreen()) backgroundColor = Color(uiColor: .mvmGreen)
} }
if headline.textColor == nil { if headline.textColor == nil {
headline.textColor = Color(uiColor: .white) headline.textColor = Color(uiColor: .mvmWhite)
} }
if body?.textColor == nil { if body?.textColor == nil {
body?.textColor = Color(uiColor: .white) body?.textColor = Color(uiColor: .mvmWhite)
} }
if button?.style == nil { if button?.style == nil {
button?.style = .secondary button?.style = .secondary
} }
button?.size = .tiny button?.size = .tiny
button?.enabledTextColor = Color(uiColor: .white) button?.enabledTextColor = Color(uiColor: .mvmWhite)
button?.enabledBorderColor = Color(uiColor: .white) button?.enabledBorderColor = Color(uiColor: .mvmWhite)
} }
//--------------------------------------------------
// MARK: - Coding Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case accessibilityIdentifier
case headline case headline
case body case body
case button case button
case closeButton case closeButton
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
headline = try typeContainer.decode(LabelModel.self, forKey: .headline) headline = try typeContainer.decode(LabelModel.self, forKey: .headline)
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button)
@ -63,6 +83,7 @@ open class NotificationModel: MoleculeModelProtocol {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(headline, forKey: .headline) try container.encode(headline, forKey: .headline)
try container.encodeIfPresent(body, forKey: .body) try container.encodeIfPresent(body, forKey: .body)
try container.encodeIfPresent(button, forKey: .button) try container.encodeIfPresent(button, forKey: .button)

View File

@ -26,6 +26,8 @@ import Foundation
adjustsImageWhenHighlighted = false adjustsImageWhenHighlighted = false
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "AccCloseButton") accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "AccCloseButton")
setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
heightAnchor.constraint(equalToConstant: 16.0).isActive = true
widthAnchor.constraint(equalToConstant: 16.0).isActive = true
} }
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {

View File

@ -19,19 +19,15 @@ open class HeadlineBody: View {
// MARK: - Constraints // MARK: - Constraints
//-------------------------------------------------- //--------------------------------------------------
var spaceBetweenLabelsConstant = PaddingOne var spaceBetweenLabelsConstant = Padding.One
var spaceBetweenLabels: NSLayoutConstraint? var spaceBetweenLabels: NSLayoutConstraint?
var leftConstraintTitle: NSLayoutConstraint?
var rightConstraintTitle: NSLayoutConstraint?
var leftConstraintMessage: NSLayoutConstraint?
var rightConstraintMessage: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
func hasText() -> Bool { func hasText() -> Bool {
return headlineLabel.hasText || messageLabel.hasText headlineLabel.hasText || messageLabel.hasText
} }
// MARK: - Styling // MARK: - Styling
@ -54,25 +50,25 @@ open class HeadlineBody: View {
} }
} }
func styleLandingPageHeader() { public func styleLandingPageHeader() {
headlineLabel.setFontStyle(.Title2XLarge) headlineLabel.setFontStyle(.Title2XLarge)
messageLabel.setFontStyle(.RegularBodySmall) messageLabel.setFontStyle(.RegularBodySmall)
spaceBetweenLabelsConstant = PaddingTwo spaceBetweenLabelsConstant = Padding.Two
} }
func stylePageHeader() { public func stylePageHeader() {
headlineLabel.setFontStyle(.BoldTitleLarge) headlineLabel.setFontStyle(.BoldTitleLarge)
messageLabel.setFontStyle(.RegularBodySmall) messageLabel.setFontStyle(.RegularBodySmall)
spaceBetweenLabelsConstant = PaddingOne spaceBetweenLabelsConstant = Padding.One
} }
func styleListItem() { public func styleListItem() {
headlineLabel.setFontStyle(.BoldBodySmall) headlineLabel.setFontStyle(.BoldBodySmall)
messageLabel.setFontStyle(.RegularBodySmall) messageLabel.setFontStyle(.RegularBodySmall)
spaceBetweenLabelsConstant = 0 spaceBetweenLabelsConstant = 0
} }
func styleListItemDivider() { public func styleListItemDivider() {
headlineLabel.setFontStyle(.BoldTitleMedium) headlineLabel.setFontStyle(.BoldTitleMedium)
messageLabel.setFontStyle(.RegularBodySmall) messageLabel.setFontStyle(.RegularBodySmall)
spaceBetweenLabelsConstant = 0 spaceBetweenLabelsConstant = 0
@ -86,48 +82,41 @@ open class HeadlineBody: View {
super.setupView() super.setupView()
backgroundColor = .clear backgroundColor = .clear
clipsToBounds = true isAccessibilityElement = false
shouldGroupAccessibilityChildren = true
accessibilityElements = [headlineLabel, messageLabel]
let view = MVMCoreUICommonViewsUtility.commonView() addSubview(headlineLabel)
addSubview(view) addSubview(messageLabel)
NSLayoutConstraint.constraintPinSubview(toSuperview: view)
view.isAccessibilityElement = false
view.shouldGroupAccessibilityChildren = true
view.accessibilityElements = [headlineLabel, messageLabel]
view.addSubview(headlineLabel)
view.addSubview(messageLabel)
headlineLabel.setContentCompressionResistancePriority(.required, for: .vertical)
headlineLabel.setContentHuggingPriority(.required, for: .vertical) headlineLabel.setContentHuggingPriority(.required, for: .vertical)
messageLabel.setContentCompressionResistancePriority(.required, for: .vertical)
messageLabel.setContentHuggingPriority(.required, for: .vertical) messageLabel.setContentHuggingPriority(.required, for: .vertical)
view.setContentHuggingPriority(.required, for: .vertical)
headlineLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true headlineLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true
spaceBetweenLabels = messageLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: spaceBetweenLabelsConstant) spaceBetweenLabels = messageLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: spaceBetweenLabelsConstant)
spaceBetweenLabels?.isActive = true spaceBetweenLabels?.isActive = true
leftConstraintTitle = headlineLabel.leftAnchor.constraint(equalTo: view.leftAnchor) NSLayoutConstraint.activate([
leftConstraintTitle?.isActive = true headlineLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor),
rightConstraintTitle = view.rightAnchor.constraint(equalTo: headlineLabel.rightAnchor) messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
rightConstraintTitle?.isActive = true trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor),
bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor)
leftConstraintMessage = messageLabel.leftAnchor.constraint(equalTo: view.leftAnchor) ])
leftConstraintMessage?.isActive = true
rightConstraintMessage = view.rightAnchor.constraint(equalTo: messageLabel.rightAnchor)
rightConstraintMessage?.isActive = true
view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor).isActive = true
} }
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
setSpacing()
headlineLabel.updateView(size) headlineLabel.updateView(size)
messageLabel.updateView(size) messageLabel.updateView(size)
setSpacing()
// Provide the label additional size information to help calculate its intrinsic height.
let padding = MFStyler.defaultHorizontalPadding(forSize: size) * 2
messageLabel.preferredMaxLayoutWidth = size - padding
} }
//-------------------------------------------------- //--------------------------------------------------
@ -146,19 +135,18 @@ open class HeadlineBody: View {
// MARK: - MoleculeViewProtocol // MARK: - MoleculeViewProtocol
//-------------------------------------------------- //--------------------------------------------------
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { public override class func estimatedHeight(with model: MoleculeModelProtocol,
return 58 _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 58 }
}
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
guard let headlineBodyModel = model as? HeadlineBodyModel else { return } guard let model = model as? HeadlineBodyModel else { return }
style(with: headlineBodyModel.style) style(with: model.style)
headlineLabel.setOptional(with: headlineBodyModel.headline, delegateObject, additionalData) headlineLabel.setOptional(with: model.headline, delegateObject, additionalData)
messageLabel.setOptional(with: headlineBodyModel.body, delegateObject, additionalData) messageLabel.setOptional(with: model.body, delegateObject, additionalData)
} }
open override func reset() { open override func reset() {

View File

@ -16,7 +16,7 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
open var stackItems: [UIView] = [] open var stackItems: [UIView] = []
open var stackModel: T? { open var stackModel: T? {
get { return model as? T } get { model as? T }
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -0,0 +1,23 @@
//
// AccessibilityModelProtocol.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/3/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol AccessibilityModelProtocol {
var accessibilityIdentifier: String? { get set }
}
public extension AccessibilityModelProtocol {
var accessibilityIdentifier: String? {
get { nil }
set { }
}
}

View File

@ -7,22 +7,16 @@ public enum MolecularError: Swift.Error {
} }
public protocol MoleculeModelProtocol: ModelProtocol { public protocol MoleculeModelProtocol: ModelProtocol, AccessibilityModelProtocol {
var moleculeName: String { get } var moleculeName: String { get }
var backgroundColor: Color? { get set } var backgroundColor: Color? { get set }
} }
public extension MoleculeModelProtocol { public extension MoleculeModelProtocol {
var moleculeName: String { var moleculeName: String { Self.identifier }
get { return Self.identifier }
}
static var categoryName: String { static var categoryName: String { "\(MoleculeModelProtocol.self)" }
return "\(MoleculeModelProtocol.self)"
}
static var categoryCodingKey: String { static var categoryCodingKey: String { "moleculeName" }
return "moleculeName"
}
} }

View File

@ -29,14 +29,10 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate {
super.handleNewData() super.handleNewData()
closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { [weak self] _ in closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { [weak self] _ in
guard let self = self else { guard let self = self else { return }
return let closeAction = (self.templateModel as? ModalListPageTemplateModel)?.closeAction ??
} ActionBackModel()
guard let model = self.templateModel as? ModalListPageTemplateModel, let actionMap = model.closeAction else { MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar)
MVMCoreActionHandler.shared()?.handleAction(with: ActionBackModel().toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar)
return
}
MVMCoreActionHandler.shared()?.handleAction(with: actionMap.toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar)
}) })
} }

View File

@ -18,15 +18,10 @@ open class ModalMoleculeStackTemplate: MoleculeStackTemplate {
override open func handleNewData() { override open func handleNewData() {
super.handleNewData() super.handleNewData()
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in _ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in
guard let self = self else { guard let self = self else { return }
return let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ??
} ActionBackModel()
MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar)
guard let model = self.templateModel as? ModalStackPageTemplateModel, let actionMap = model.closeAction else {
MVMCoreActionHandler.shared()?.handleAction(with: ActionBackModel().toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar)
return
}
MVMCoreActionHandler.shared()?.handleAction(with: actionMap.toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar)
}) })
} }
} }

View File

@ -19,12 +19,9 @@ open class ModalSectionListTemplate: SectionListTemplate {
super.handleNewData() super.handleNewData()
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in _ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in
guard let self = self else { return } guard let self = self else { return }
guard let model = self.templateModel as? ModalSectionListTemplateModel, let closeAction = (self.templateModel as? ModalSectionListTemplateModel)?.closeAction ??
let actionMap = model.closeAction else { ActionBackModel()
MVMCoreActionHandler.shared()?.handleAction(with: ActionBackModel().toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar) MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar)
return
}
MVMCoreActionHandler.shared()?.handleAction(with: actionMap.toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar)
}) })
} }
} }

View File

@ -19,7 +19,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
var observer: NSKeyValueObservation? var observer: NSKeyValueObservation?
public var templateModel: ListPageTemplateModel? public var templateModel: ListPageTemplateModel?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Computed Properties // MARK: - Computed Properties
//-------------------------------------------------- //--------------------------------------------------
@ -41,7 +41,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
open override func parsePageJSON() throws { open override func parsePageJSON() throws {
try parseTemplate(json: loadObject?.pageJSON) try parseTemplate(json: loadObject?.pageJSON)
try super.parsePageJSON() try super.parsePageJSON()
@ -54,8 +54,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open override func viewForTop() -> UIView { open override func viewForTop() -> UIView {
guard let headerModel = templateModel?.header, guard let headerModel = templateModel?.header,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar)
else { return super.viewForTop() } else { return super.viewForTop() }
// Temporary, Default the horizontal padding // Temporary, Default the horizontal padding
if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil {
@ -67,8 +67,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
override open func viewForBottom() -> UIView { override open func viewForBottom() -> UIView {
guard let footerModel = templateModel?.footer, guard let footerModel = templateModel?.footer,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar)
else { return super.viewForBottom() } else { return super.viewForBottom() }
return molecule return molecule
} }
@ -86,7 +86,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
//Handle scroll //Handle scroll
handleScrollToSpecificRow() handleScrollToSpecificRow()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Handle scroll to spefic row // MARK: - Handle scroll to spefic row
@ -117,12 +117,12 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
guard let moleculeInfo = getMoleculeInfo(for: indexPath), guard let moleculeInfo = getMoleculeInfo(for: indexPath),
let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject)
else { return 0 } else { return 0 }
return estimatedHeight return estimatedHeight
} }
open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return moleculesInfo?.count ?? 0 return moleculesInfo?.count ?? 0
} }
@ -130,8 +130,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let moleculeInfo = getMoleculeInfo(for: indexPath), guard let moleculeInfo = getMoleculeInfo(for: indexPath),
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier)
else { return UITableViewCell() } else { return UITableViewCell() }
(cell as? MoleculeViewProtocol)?.reset() (cell as? MoleculeViewProtocol)?.reset()
(cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath) (cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath)
@ -222,8 +222,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
guard let listItem = listItem, guard let listItem = listItem,
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem)
else { return nil } else { return nil }
let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName

View File

@ -14,6 +14,14 @@ import Foundation
return 0 return 0
} }
open override func loadView() {
super.loadView()
// The height is used to keep the bottom view at the bottom.
if let contentView = contentView, let scrollView = scrollView {
contentView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.safeAreaLayoutGuide.heightAnchor).isActive = true
}
}
open override func handleNewData() { open override func handleNewData() {
super.handleNewData() super.handleNewData()
heightConstraint?.isActive = true heightConstraint?.isActive = true

View File

@ -8,6 +8,7 @@
public typealias BarButtonAction = (BarButtonItem) -> () public typealias BarButtonAction = (BarButtonItem) -> ()
@objc class ActionDelegate: NSObject { @objc class ActionDelegate: NSObject {
var buttonAction: BarButtonAction? var buttonAction: BarButtonAction?
@objc func callActionBlock(_ sender: BarButtonItem) { @objc func callActionBlock(_ sender: BarButtonItem) {
@ -16,7 +17,6 @@ public typealias BarButtonAction = (BarButtonItem) -> ()
} }
@objcMembers open class BarButtonItem: UIBarButtonItem, MFButtonProtocol { @objcMembers open class BarButtonItem: UIBarButtonItem, MFButtonProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Delegate // MARK: - Delegate
//-------------------------------------------------- //--------------------------------------------------
@ -43,4 +43,3 @@ public typealias BarButtonAction = (BarButtonItem) -> ()
} }
} }
} }

View File

@ -8,6 +8,7 @@
public typealias ButtonAction = (Button) -> () public typealias ButtonAction = (Button) -> ()
@objcMembers open class Button: UIButton, MFButtonProtocol, MoleculeViewProtocol { @objcMembers open class Button: UIButton, MFButtonProtocol, MoleculeViewProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
@ -65,7 +66,7 @@ public typealias ButtonAction = (Button) -> ()
/// Adds a block to be performed for the given event. /// Adds a block to be performed for the given event.
open func addActionBlock(event: Event, _ buttonBlock: @escaping ButtonAction) { open func addActionBlock(event: Event, _ buttonBlock: @escaping ButtonAction) {
self.buttonAction = buttonBlock self.buttonAction = buttonBlock
addTarget(self, action: #selector(callActionBlock(_:)), for: event) addTarget(self, action: #selector(callActionBlock), for: event)
} }
@objc func callActionBlock(_ sender: Button) { @objc func callActionBlock(_ sender: Button) {
@ -103,6 +104,10 @@ public typealias ButtonAction = (Button) -> ()
self.backgroundColor = backgroundColor.uiColor self.backgroundColor = backgroundColor.uiColor
} }
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
if let model = model as? EnableableModelProtocol { if let model = model as? EnableableModelProtocol {
isEnabled = model.enabled isEnabled = model.enabled
} }
@ -119,25 +124,23 @@ public typealias ButtonAction = (Button) -> ()
// MARK: Overridables // MARK: Overridables
// Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead. // Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead.
open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
return model.moleculeName model.moleculeName
} }
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
return nil
}
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
return nil
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Accessibility // MARK: - Accessibility
//-------------------------------------------------- //--------------------------------------------------
open override func accessibilityActivate() -> Bool { open override func accessibilityActivate() -> Bool {
guard isEnabled else { return false }
buttonAction?(self) buttonAction?(self)
return buttonAction != nil return buttonAction != nil
} }
} }
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
@ -160,6 +163,6 @@ extension Button: MVMCoreViewProtocol {
extension Button: AppleGuidelinesProtocol { extension Button: AppleGuidelinesProtocol {
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return Self.acceptablyOutsideBounds(point: point, bounds: bounds) Self.acceptablyOutsideBounds(point: point, bounds: bounds)
} }
} }

View File

@ -51,9 +51,14 @@ import UIKit
// MARK:- MoleculeViewProtocol // MARK:- MoleculeViewProtocol
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.model = model self.model = model
if let backgroundColor = model.backgroundColor { if let backgroundColor = model.backgroundColor {
self.backgroundColor = backgroundColor.uiColor self.backgroundColor = backgroundColor.uiColor
} }
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
} }
open func reset() { open func reset() {
@ -63,22 +68,18 @@ import UIKit
// MARK: Overridables // MARK: Overridables
// Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead. // Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead.
open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
return model.moleculeName model.moleculeName
} }
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
return nil
}
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
return nil
}
} }
// MARK: - AppleGuidelinesProtocol // MARK: - AppleGuidelinesProtocol
extension Control: AppleGuidelinesProtocol { extension Control: AppleGuidelinesProtocol {
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return Self.acceptablyOutsideBounds(point: point, bounds: bounds) Self.acceptablyOutsideBounds(point: point, bounds: bounds)
} }
} }

View File

@ -53,33 +53,35 @@ open class ImageView: UIImageView, MoleculeViewProtocol {
} }
public func reset() { public func reset() {
backgroundColor = .clear backgroundColor = .clear
} }
public func setAsMolecule() { } public func setAsMolecule() { }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - ModelMoleculeViewProtocol // MARK: - ModelMoleculeViewProtocol
//-------------------------------------------------- //--------------------------------------------------
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.model = model self.model = model
if let backgroundColor = model.backgroundColor { if let backgroundColor = model.backgroundColor {
self.backgroundColor = backgroundColor.uiColor self.backgroundColor = backgroundColor.uiColor
} }
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
} }
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
return model?.moleculeName model?.moleculeName
} }
open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
return nil
}
open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
return nil
}
} }
// MARK:- MVMCoreViewProtocol // MARK:- MVMCoreViewProtocol
@ -94,4 +96,3 @@ extension ImageView: MVMCoreViewProtocol {
MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0) MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0)
} }
} }

View File

@ -6,7 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers open class SectionHeaderFooterView: UITableViewHeaderFooterView, MoleculeViewProtocol { @objcMembers open class SectionHeaderFooterView: UITableViewHeaderFooterView, MoleculeViewProtocol {
//-------------------------------------------------- //--------------------------------------------------
@ -43,10 +42,16 @@ import Foundation
//-------------------------------------------------- //--------------------------------------------------
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.model = model self.model = model
if let backgroundColor = model.backgroundColor { if let backgroundColor = model.backgroundColor {
contentView.backgroundColor = backgroundColor.uiColor contentView.backgroundColor = backgroundColor.uiColor
} }
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
} }
open func reset() { open func reset() {
@ -56,16 +61,12 @@ import Foundation
// MARK: Overridables // MARK: Overridables
// Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead. // Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead.
open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
return model.moleculeName model.moleculeName
} }
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
return nil
}
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
return nil
}
} }
// MARK:- MVMCoreViewProtocol // MARK:- MVMCoreViewProtocol

View File

@ -11,7 +11,7 @@ import Foundation
@objcMembers open class TableView: UITableView { @objcMembers open class TableView: UITableView {
/// A block that gets called on tableview frame changes /// A block that gets called on tableview frame changes
public var frameChangeAction: (() -> Void)? public var frameChangeAction: (() -> ())?
private var previousFrame = CGRect.zero private var previousFrame = CGRect.zero

Some files were not shown because too many files have changed in this diff Show More