Merge branch 'develop' into bugfix/CXTDT-139439
* develop: (75 commits) spelling fix default moved by request updates asd latest change noself put back multi select latest current move flag set debug cleanup active fix ugh timing workaround timing issue restructure video load image needed Active listener ...
This commit is contained in:
commit
ef77483f8b
@ -62,6 +62,8 @@
|
||||
01EB369323609801006832FA /* HeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368C23609801006832FA /* HeaderModel.swift */; };
|
||||
01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368D23609801006832FA /* HeadlineBodyModel.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 */; };
|
||||
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.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 */; };
|
||||
0A7EF85F23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.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 */; };
|
||||
0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.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 */; };
|
||||
0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091C2433796500D2E6C0 /* CarouselIndicator.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 */; };
|
||||
0AB000BC24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.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 */; };
|
||||
0AD93A9F24C0AA5100E56A97 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7918F423F5E7EA00772FF4 /* ImageView.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 */; };
|
||||
0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */; };
|
||||
0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB423FF18D2004C5109 /* Arrow.swift */; };
|
||||
@ -346,6 +351,8 @@
|
||||
D23EA800247EBD6C00D60C34 /* LabelBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */; };
|
||||
D23EA802247EBED400D60C34 /* ImageBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA801247EBED400D60C34 /* ImageBarButtonItem.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 */; };
|
||||
D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */; };
|
||||
D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; };
|
||||
@ -402,6 +409,11 @@
|
||||
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, ); }; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
@ -604,6 +616,8 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -637,7 +651,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -650,6 +664,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -657,6 +672,8 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -890,6 +907,8 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -944,6 +963,11 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1109,6 +1133,7 @@
|
||||
011B58EE23A2AA850085F53C /* ModelProtocols */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */,
|
||||
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */,
|
||||
014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */,
|
||||
D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */,
|
||||
@ -1166,6 +1191,39 @@
|
||||
path = FormUIHelpers;
|
||||
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 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1225,6 +1283,8 @@
|
||||
children = (
|
||||
27F973522466074500CAB5C5 /* PageBehavior.swift */,
|
||||
27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */,
|
||||
D24918F525D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift */,
|
||||
D24918F925D5ADBA00CAB4B1 /* PageScrolledClosureBehavior.swift */,
|
||||
);
|
||||
path = Behaviors;
|
||||
sourceTree = "<group>";
|
||||
@ -1676,6 +1736,8 @@
|
||||
D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */,
|
||||
D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */,
|
||||
D253BB9B245874F8002DE544 /* BGImageMolecule.swift */,
|
||||
D29C558925C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift */,
|
||||
D29C558C25C05C990082E7D6 /* BGVideoImageMolecule.swift */,
|
||||
);
|
||||
path = OtherContainers;
|
||||
sourceTree = "<group>";
|
||||
@ -1710,6 +1772,8 @@
|
||||
D2092348244A51D40044AD09 /* RadioSwatchModel.swift */,
|
||||
AAB9C109243496DD00151545 /* RadioSwatch.swift */,
|
||||
AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */,
|
||||
D260105223CEA61600764D80 /* ToggleModel.swift */,
|
||||
0AA33B392398524F0067DD0F /* Toggle.swift */,
|
||||
AAA7CD68250641F90045B959 /* HeartModel.swift */,
|
||||
AAA7CD6A250642080045B959 /* Heart.swift */,
|
||||
);
|
||||
@ -2007,8 +2071,6 @@
|
||||
D28A838223CCBD3F00DFE4FC /* WheelModel.swift */,
|
||||
943784F3236B77BB006A1E82 /* Wheel.swift */,
|
||||
943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */,
|
||||
D260105223CEA61600764D80 /* ToggleModel.swift */,
|
||||
0AA33B392398524F0067DD0F /* Toggle.swift */,
|
||||
0AE98BB623FF18E9004C5109 /* ArrowModel.swift */,
|
||||
0AE98BB423FF18D2004C5109 /* Arrow.swift */,
|
||||
94382085243238D100B43AF3 /* WebViewModel.swift */,
|
||||
@ -2020,6 +2082,9 @@
|
||||
AA37CBD42519072F0027344C /* Stars.swift */,
|
||||
AA07EA902510A442009A2AE3 /* StarModel.swift */,
|
||||
AA07EA922510A451009A2AE3 /* Star.swift */,
|
||||
D29C559525C099630082E7D6 /* VideoDataManager.swift */,
|
||||
D29C559225C0992D0082E7D6 /* VideoModel.swift */,
|
||||
D29C558F25C095210082E7D6 /* Video.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@ -2027,23 +2092,18 @@
|
||||
D29DF22B21E6A0FA003B2FB9 /* TextFields */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */,
|
||||
0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */,
|
||||
0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */,
|
||||
0A21DB7E235DECC500C160A2 /* EntryField.swift */,
|
||||
0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */,
|
||||
0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */,
|
||||
0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */,
|
||||
0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */,
|
||||
0A8321AE2355FE9500CB7F00 /* DigitBox.swift */,
|
||||
0A7EF86023D8AC2500B2AAD1 /* DigitEntryFieldModel.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 */,
|
||||
0A25209524645AFD000FA9F6 /* TextViewEntryField.swift */,
|
||||
0A0FEC7125D4246000AF2548 /* Dropdown Fields */,
|
||||
);
|
||||
path = TextFields;
|
||||
sourceTree = "<group>";
|
||||
@ -2157,6 +2217,7 @@
|
||||
D2B18B7D236090D500A9AEDC /* BaseClasses */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0A5D59C323AD488600EFD9E9 /* Protocols */,
|
||||
0A6682B3243769C700AD3CA1 /* TextView.swift */,
|
||||
C003506023AA94CD00B6AC29 /* Button.swift */,
|
||||
D2B18B7E2360913400A9AEDC /* Control.swift */,
|
||||
@ -2167,7 +2228,6 @@
|
||||
BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */,
|
||||
D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */,
|
||||
D264FAA92440F97600D98315 /* CollectionView.swift */,
|
||||
0A5D59C323AD488600EFD9E9 /* Protocols */,
|
||||
0A7918F423F5E7EA00772FF4 /* ImageView.swift */,
|
||||
D272F5F82473163100BD1A8F /* BarButtonItem.swift */,
|
||||
D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */,
|
||||
@ -2449,6 +2509,7 @@
|
||||
D264FAAA2440F97600D98315 /* CollectionView.swift in Sources */,
|
||||
AAC23FAD24D92A0D009208DF /* ListThreeColumnSpeedTestModel.swift in Sources */,
|
||||
BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */,
|
||||
0AE277EC25D2EE310048A38D /* MultiItemDropdownEntryFieldModel.swift in Sources */,
|
||||
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */,
|
||||
3265B30424BCA749000D154B /* HeadersH1NoButtonsBodyText.swift in Sources */,
|
||||
AAA7CD69250641F90045B959 /* HeartModel.swift in Sources */,
|
||||
@ -2463,6 +2524,7 @@
|
||||
D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */,
|
||||
94382086243238D100B43AF3 /* WebViewModel.swift in Sources */,
|
||||
D28764F9245A327200CB882D /* TwoLinkView.swift in Sources */,
|
||||
0AE277E925D2ED4B0048A38D /* MultiItemDropdownEntryField.swift in Sources */,
|
||||
D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */,
|
||||
0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */,
|
||||
D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */,
|
||||
@ -2563,6 +2625,7 @@
|
||||
D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */,
|
||||
014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */,
|
||||
AAC23FAF24D92A1E009208DF /* ListThreeColumnSpeedTest.swift in Sources */,
|
||||
0A0FEC7425D42A5E00AF2548 /* BaseItemPickerEntryField.swift in Sources */,
|
||||
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
|
||||
D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */,
|
||||
D2ED27ED254B0CE700A1C293 /* ActionPopupModel.swift in Sources */,
|
||||
@ -2678,6 +2741,7 @@
|
||||
BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */,
|
||||
017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */,
|
||||
323AC96C24C837FF00F8E4C4 /* ListThreeColumnBillChanges.swift in Sources */,
|
||||
0A0FEC7825D42A8500AF2548 /* BaseItemPickerEntryFieldModel.swift in Sources */,
|
||||
D28A837923C7D5BC00DFE4FC /* PageModelProtocol.swift in Sources */,
|
||||
D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */,
|
||||
017BEB7B236763000024EF95 /* LineModel.swift in Sources */,
|
||||
@ -2685,6 +2749,7 @@
|
||||
94C2D9A523872C350006CF46 /* LabelAttributeFontModel.swift in Sources */,
|
||||
011D958724042492000E3791 /* FormFieldProtocol.swift in Sources */,
|
||||
011D95AF2407266E000E3791 /* RadioButtonModel.swift in Sources */,
|
||||
D24918F625D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift in Sources */,
|
||||
D20492A624329CE200A5EED6 /* LoadImageView.swift in Sources */,
|
||||
017BEB7F23676E870024EF95 /* MoleculeObjectMapping.swift in Sources */,
|
||||
D274CA332236A78900B01B62 /* FooterView.swift in Sources */,
|
||||
@ -2742,6 +2807,7 @@
|
||||
AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */,
|
||||
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */,
|
||||
D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */,
|
||||
D29C558A25C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift in Sources */,
|
||||
8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */,
|
||||
BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */,
|
||||
AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */,
|
||||
@ -2769,6 +2835,7 @@
|
||||
012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */,
|
||||
D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */,
|
||||
0AE14F64238315D2005417F8 /* TextField.swift in Sources */,
|
||||
D24918FA25D5ADBB00CAB4B1 /* PageScrolledClosureBehavior.swift in Sources */,
|
||||
0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */,
|
||||
D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */,
|
||||
0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */,
|
||||
@ -2781,6 +2848,7 @@
|
||||
D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */,
|
||||
AAB7EDF1246ADA2A00E54929 /* ListProgressBarThin.swift in Sources */,
|
||||
8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */,
|
||||
D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */,
|
||||
D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */,
|
||||
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */,
|
||||
AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */,
|
||||
@ -2844,8 +2912,10 @@
|
||||
012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */,
|
||||
52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */,
|
||||
D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */,
|
||||
0AA4D2E125CAEC72008DB32D /* AccessibilityModelProtocol.swift in Sources */,
|
||||
C003506123AA94CD00B6AC29 /* Button.swift in Sources */,
|
||||
DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */,
|
||||
D29C559025C095210082E7D6 /* Video.swift in Sources */,
|
||||
D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */,
|
||||
AA104B1C24474A76004D2810 /* HeadersH2ButtonsModel.swift in Sources */,
|
||||
0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */,
|
||||
@ -2859,7 +2929,7 @@
|
||||
D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */,
|
||||
BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */,
|
||||
D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */,
|
||||
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */,
|
||||
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownFieldModel.swift in Sources */,
|
||||
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */,
|
||||
D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */,
|
||||
D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */,
|
||||
@ -2871,6 +2941,7 @@
|
||||
BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */,
|
||||
C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */,
|
||||
D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */,
|
||||
D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */,
|
||||
8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */,
|
||||
D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */,
|
||||
012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */,
|
||||
@ -2888,6 +2959,7 @@
|
||||
AA633B3324989ED500731E80 /* HeadersH2PricingTwoRows.swift in Sources */,
|
||||
01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */,
|
||||
0A6682A22434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift in Sources */,
|
||||
D29C558D25C05C990082E7D6 /* BGVideoImageMolecule.swift in Sources */,
|
||||
EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */,
|
||||
0105618D224BBE7700E1557D /* FormValidator.swift in Sources */,
|
||||
01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */,
|
||||
@ -2961,6 +3033,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@ -3026,6 +3099,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
||||
@ -18,6 +18,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
|
||||
|
||||
public static var identifier: String = "button"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var title: String
|
||||
public var action: ActionModelProtocol
|
||||
public var enabled: Bool = true
|
||||
@ -93,27 +94,27 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
|
||||
//--------------------------------------------------
|
||||
|
||||
public func enabled_fillColor() -> UIColor? {
|
||||
return (inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor
|
||||
(inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor
|
||||
}
|
||||
|
||||
public func enabled_textColor() -> UIColor? {
|
||||
return (inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor
|
||||
(inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor
|
||||
}
|
||||
|
||||
public func enabled_borderColor() -> UIColor? {
|
||||
return (inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor
|
||||
(inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor
|
||||
}
|
||||
|
||||
public func disabled_fillColor() -> UIColor? {
|
||||
return (inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor
|
||||
(inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor
|
||||
}
|
||||
|
||||
public func disabled_textColor() -> UIColor? {
|
||||
return (inverted ? disabledTextColor_inverted : disabledTextColor)?.uiColor
|
||||
(inverted ? disabledTextColor_inverted : disabledTextColor)?.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.
|
||||
@ -172,6 +173,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case title
|
||||
case inverted
|
||||
case action
|
||||
@ -195,6 +197,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
title = try typeContainer.decode(String.self, forKey: .title)
|
||||
action = try typeContainer.decodeModel(codingKey: .action)
|
||||
|
||||
@ -252,6 +255,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
|
||||
try container.encode(inverted, forKey: .inverted)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encodeIfPresent(enabledFillColor, forKey: .fillColor)
|
||||
try container.encodeIfPresent(enabledTextColor, forKey: .textColor)
|
||||
try container.encodeIfPresent(enabledBorderColor, forKey: .borderColor)
|
||||
|
||||
@ -127,7 +127,6 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
}
|
||||
|
||||
public func updateCaretSpacing(_ spacing: CGFloat) {
|
||||
|
||||
caretSpacingConstraint?.constant = spacing
|
||||
}
|
||||
|
||||
@ -151,15 +150,9 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
setTitle(model.title, for: .normal)
|
||||
}
|
||||
|
||||
public func needsToBeConstrained() -> Bool {
|
||||
return true
|
||||
}
|
||||
public func needsToBeConstrained() -> Bool { true }
|
||||
|
||||
open func horizontalAlignment() -> UIStackView.Alignment {
|
||||
return .leading
|
||||
}
|
||||
open func horizontalAlignment() -> UIStackView.Alignment { .leading }
|
||||
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return 10.5
|
||||
}
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 10.5 }
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea
|
||||
|
||||
public static var identifier: String = "caretLink"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var title: String
|
||||
public var action: ActionModelProtocol
|
||||
public var enabledColor: Color = Color(uiColor: .mvmBlack)
|
||||
@ -41,6 +42,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case title
|
||||
case action
|
||||
case enabledColor_inverted
|
||||
@ -60,6 +62,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
title = try typeContainer.decode(String.self, forKey: .title)
|
||||
|
||||
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(title, forKey: .title)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
try container.encode(enabled, forKey: .enabledColor)
|
||||
try container.encodeIfPresent(disabledColor, forKey: .disabledColor)
|
||||
|
||||
@ -11,18 +11,18 @@ import UIKit
|
||||
|
||||
@objcMembers open class Link: Button {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
// MARK: - Draw
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func draw(_ rect: CGRect) {
|
||||
|
||||
guard let textRect = titleLabel?.frame else { return }
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
guard let textRect = titleLabel?.frame,
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
else { return }
|
||||
|
||||
// Set line to the same color as the text
|
||||
if let color = titleLabel?.textColor?.cgColor {
|
||||
context?.setStrokeColor(color)
|
||||
context.setStrokeColor(color)
|
||||
}
|
||||
|
||||
// x should be according to the text, not the button
|
||||
@ -31,9 +31,9 @@ import UIKit
|
||||
// Line is 1 point below the text
|
||||
let y = textRect.origin.y + textRect.size.height + 1
|
||||
|
||||
context?.move(to: CGPoint(x: x, y: y))
|
||||
context?.addLine(to: CGPoint(x: x + textRect.size.width, y: y))
|
||||
context?.strokePath()
|
||||
context.move(to: CGPoint(x: x, y: y))
|
||||
context.addLine(to: CGPoint(x: x + textRect.size.width, y: y))
|
||||
context.strokePath()
|
||||
}
|
||||
|
||||
open override var intrinsicContentSize: CGSize {
|
||||
@ -58,9 +58,7 @@ import UIKit
|
||||
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return 31
|
||||
}
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 31 }
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
@ -69,16 +67,12 @@ extension Link {
|
||||
open override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
var width = size
|
||||
if MVMCoreGetterUtility.fequal(a: Float.leastNormalMagnitude, b: Float(size)) {
|
||||
width = MVMCoreUIUtility.getWidth()
|
||||
}
|
||||
|
||||
self.titleLabel?.font = MFStyler.fontB2(forWidth: width)
|
||||
var width = size
|
||||
if MVMCoreGetterUtility.fequal(a: Float.leastNormalMagnitude, b: Float(size)) {
|
||||
width = MVMCoreUIUtility.getWidth()
|
||||
}
|
||||
|
||||
titleLabel?.font = MFStyler.fontB2(forWidth: width)
|
||||
}
|
||||
|
||||
open override func setupView() {
|
||||
@ -98,7 +92,5 @@ extension Link {
|
||||
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||
extension Link: MVMCoreUIViewConstrainingProtocol {
|
||||
|
||||
open func horizontalAlignment() -> UIStackView.Alignment {
|
||||
return .leading
|
||||
}
|
||||
open func horizontalAlignment() -> UIStackView.Alignment { .leading }
|
||||
}
|
||||
|
||||
@ -14,11 +14,10 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public class var identifier: String {
|
||||
return "link"
|
||||
}
|
||||
public class var identifier: String { "link" }
|
||||
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var title: String
|
||||
public var action: ActionModelProtocol
|
||||
public var enabled = true
|
||||
@ -44,6 +43,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case title
|
||||
case action
|
||||
case enabled
|
||||
@ -62,6 +62,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
title = try typeContainer.decode(String.self, forKey: .title)
|
||||
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(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
try container.encode(inverted, forKey: .inverted)
|
||||
try container.encode(enabled, forKey: .enabled)
|
||||
|
||||
@ -18,7 +18,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
var size = MVMCoreUIUtility.getWidth()
|
||||
|
||||
var buttonModel: ButtonModel? {
|
||||
get { return model as? ButtonModel }
|
||||
get { model as? ButtonModel }
|
||||
}
|
||||
|
||||
/// Need to re-style on set.
|
||||
@ -27,9 +27,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
}
|
||||
|
||||
open var buttonSize: Styler.Button.Size = .standard {
|
||||
didSet {
|
||||
buttonModel?.size = buttonSize
|
||||
}
|
||||
didSet { buttonModel?.size = buttonSize }
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -47,12 +45,12 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
//--------------------------------------------------
|
||||
|
||||
public var enabledTitleColor: UIColor? {
|
||||
get { return titleColor(for: .normal) }
|
||||
get { titleColor(for: .normal) }
|
||||
set { setTitleColor(newValue, for: .normal) }
|
||||
}
|
||||
|
||||
public var disabledTitleColor: UIColor? {
|
||||
get { return titleColor(for: .disabled) }
|
||||
get { titleColor(for: .disabled) }
|
||||
set { setTitleColor(newValue, for: .disabled) }
|
||||
}
|
||||
|
||||
@ -106,6 +104,11 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
self.disabledTitleColor = disabledTitleColor
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// Useful to detect with isHittable when performing UI testing.
|
||||
isAccessibilityElement = isEnabled
|
||||
#endif
|
||||
|
||||
if isEnabled {
|
||||
if let fillColor = buttonModel?.enabledColors.fill {
|
||||
backgroundColor = fillColor
|
||||
@ -128,7 +131,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
}
|
||||
|
||||
private func getInnerPadding() -> CGFloat {
|
||||
return getHeight() / 2.0
|
||||
getHeight() / 2.0
|
||||
}
|
||||
|
||||
private func getHeight() -> CGFloat {
|
||||
@ -181,9 +184,11 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
|
||||
guard let model = model as? ButtonModel else { return }
|
||||
setTitle(model.title, for: .normal)
|
||||
|
||||
if let size = model.size {
|
||||
buttonSize = size
|
||||
}
|
||||
|
||||
model.updateUI = { [weak self] in
|
||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
||||
self?.enableField(model.enabled)
|
||||
@ -228,9 +233,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
||||
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||
//--------------------------------------------------
|
||||
|
||||
open func horizontalAlignment() -> UIStackView.Alignment {
|
||||
return .center
|
||||
}
|
||||
open func horizontalAlignment() -> UIStackView.Alignment { .center }
|
||||
|
||||
public func enableField(_ enable: Bool) {
|
||||
isEnabled = enable
|
||||
|
||||
@ -43,7 +43,7 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
|
||||
public override var showError: Bool {
|
||||
get { return super.showError }
|
||||
get { super.showError }
|
||||
set (error) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@ -80,7 +80,7 @@ import UIKit
|
||||
|
||||
open override func setupView() {
|
||||
super.setupView()
|
||||
|
||||
|
||||
addSubview(digitField)
|
||||
digitField.delegate = self
|
||||
digitField.didDeleteDelegate = self
|
||||
@ -140,7 +140,7 @@ import UIKit
|
||||
super.updateView(size)
|
||||
|
||||
if !MVMCoreGetterUtility.fequal(a: Float(size), b: Float(previousSize)) {
|
||||
|
||||
|
||||
var width: CGFloat = 0
|
||||
var height: CGFloat = 0
|
||||
var pointSize: CGFloat = 13
|
||||
|
||||
@ -75,7 +75,7 @@ import UIKit
|
||||
private var selectedDigitBox: DigitBox?
|
||||
|
||||
public var digitEntryModel: DigitEntryFieldModel? {
|
||||
return model as? DigitEntryFieldModel
|
||||
model as? DigitEntryFieldModel
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -83,7 +83,7 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
|
||||
public override var isEnabled: Bool {
|
||||
get { return super.isEnabled }
|
||||
get { super.isEnabled }
|
||||
set (enabled) {
|
||||
digitBoxes.forEach { $0.isEnabled = enabled }
|
||||
super.isEnabled = enabled
|
||||
@ -91,7 +91,7 @@ import UIKit
|
||||
}
|
||||
|
||||
public override var showError: Bool {
|
||||
get { return super.showError }
|
||||
get { super.showError }
|
||||
set (error) {
|
||||
digitBoxes.forEach { $0.showError = error }
|
||||
super.showError = error
|
||||
@ -99,7 +99,7 @@ import UIKit
|
||||
}
|
||||
|
||||
public override var isLocked: Bool {
|
||||
get { return super.isLocked }
|
||||
get { super.isLocked }
|
||||
set (locked) {
|
||||
digitBoxes.forEach { $0.isLocked = locked }
|
||||
super.isLocked = locked
|
||||
@ -162,7 +162,7 @@ import UIKit
|
||||
|
||||
/// If you're using a MFViewController, you must set this to it
|
||||
public override weak var uiTextFieldDelegate: UITextFieldDelegate? {
|
||||
get { return textField.delegate }
|
||||
get { textField.delegate }
|
||||
set {
|
||||
textField.delegate = self
|
||||
proprietorTextDelegate = newValue
|
||||
@ -357,9 +357,7 @@ import UIKit
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return 115
|
||||
}
|
||||
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 115 }
|
||||
}
|
||||
|
||||
// MARK: - TextField Delegate
|
||||
@ -452,11 +450,11 @@ extension DigitEntryField {
|
||||
|
||||
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
|
||||
proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,12 +12,10 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "digitTextField"
|
||||
}
|
||||
public override class var identifier: String { "digitTextField" }
|
||||
|
||||
public var digits: Int = 4
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// BaseDropdownEntryField.swift
|
||||
// BaseDropdownField.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 10/23/19.
|
||||
@ -28,8 +28,9 @@ import UIKit
|
||||
}()
|
||||
|
||||
public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? {
|
||||
return model as? BaseDropdownEntryFieldModel
|
||||
model as? BaseDropdownEntryFieldModel
|
||||
}
|
||||
|
||||
var additionalData: [AnyHashable: Any]?
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -54,7 +55,7 @@ import UIKit
|
||||
|
||||
@objc required public init?(coder: NSCoder) {
|
||||
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]?) {
|
||||
@ -86,15 +87,15 @@ import UIKit
|
||||
dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
public override func dismissFieldInput(_ sender: Any?) {
|
||||
performDropdownAction()
|
||||
super.dismissFieldInput(sender)
|
||||
}
|
||||
@objc public override func dismissFieldInput(_ sender: Any?) {
|
||||
performDropdownAction()
|
||||
super.dismissFieldInput(sender)
|
||||
}
|
||||
|
||||
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)
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,9 +14,7 @@
|
||||
public var caretView: CaretViewModel?
|
||||
public var action: ActionModelProtocol?
|
||||
|
||||
public override class var identifier: String {
|
||||
return ""
|
||||
}
|
||||
public override class var identifier: String { "" }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
@ -29,7 +27,7 @@
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
@ -23,15 +23,15 @@ import UIKit
|
||||
}()
|
||||
|
||||
public var dateFormat: String? {
|
||||
get { dateDropdownModel?.dateFormat }
|
||||
set {
|
||||
guard let newValue = newValue else { return }
|
||||
dateDropdownModel?.dateFormat = newValue
|
||||
}
|
||||
get { return dateDropdownModel?.dateFormat }
|
||||
}
|
||||
|
||||
public var dateDropdownModel: DateDropdownEntryFieldModel? {
|
||||
return model as? DateDropdownEntryFieldModel
|
||||
model as? DateDropdownEntryFieldModel
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -69,6 +69,7 @@ import UIKit
|
||||
datePicker = UIDatePicker.addDatePicker(to: textField)
|
||||
datePicker?.addTarget(self, action: #selector(pickerValueChanged), for: .valueChanged)
|
||||
datePicker?.timeZone = NSTimeZone.system
|
||||
textField.inputView = datePicker
|
||||
UIToolbar.addDismissToolbar(to: textField, delegate: self, action: #selector(dismissFieldInput))
|
||||
}
|
||||
|
||||
@ -11,9 +11,7 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "dateDropdownEntryField"
|
||||
}
|
||||
public override class var identifier: String { "dateDropdownEntryField" }
|
||||
|
||||
public var dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
@ -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") ?? "")"
|
||||
}
|
||||
}
|
||||
@ -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 { "" }
|
||||
}
|
||||
@ -8,28 +8,16 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public typealias TextFieldAndPickerDelegate = (UITextFieldDelegate & UIPickerViewDelegate & UIPickerViewDataSource)
|
||||
|
||||
|
||||
open class ItemDropdownEntryField: BaseDropdownEntryField {
|
||||
open class ItemDropdownEntryField: BaseItemPickerEntryField {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
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? {
|
||||
return model as? ItemDropdownEntryFieldModel
|
||||
model as? ItemDropdownEntryFieldModel
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -61,26 +49,13 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
@objc open override func setupFieldContainerContent(_ container: UIView) {
|
||||
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
|
||||
}
|
||||
|
||||
/// Sets the textField with the first value of the available picker data.
|
||||
@objc private func setInitialValueFromPicker() {
|
||||
|
||||
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])
|
||||
text = pickerData[pickerIndex]
|
||||
itemDropdownEntryFieldModel?.selectedIndex = pickerIndex
|
||||
@ -98,9 +73,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
|
||||
|
||||
guard !pickerData.isEmpty else { return }
|
||||
|
||||
if let pickerIndex = pickerView?.selectedRow(inComponent: 0) {
|
||||
observeDropdownSelection?(pickerData[pickerIndex])
|
||||
}
|
||||
observeDropdownSelection?(pickerData[pickerView.selectedRow(inComponent: 0)])
|
||||
}
|
||||
|
||||
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 }
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- Base Picker Delegate
|
||||
extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Picker Delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
@objc public func numberOfComponents(in pickerView: UIPickerView) -> Int {
|
||||
return 1
|
||||
}
|
||||
@objc public override func numberOfComponents(in pickerView: UIPickerView) -> Int { 1 }
|
||||
|
||||
@objc public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||
return pickerData.count
|
||||
@objc public override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||
pickerData.count
|
||||
}
|
||||
|
||||
@objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
|
||||
@ -142,18 +113,3 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
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") ?? "")"
|
||||
}
|
||||
}
|
||||
@ -6,22 +6,24 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
@objcMembers open class ItemDropdownEntryFieldModel: BaseDropdownEntryFieldModel {
|
||||
@objcMembers open class ItemDropdownEntryFieldModel: BaseItemPickerEntryFieldModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "dropDown"
|
||||
}
|
||||
public override class var identifier: String { "dropDown" }
|
||||
|
||||
public var options: [String] = []
|
||||
public var selectedIndex: Int?
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Validation
|
||||
//--------------------------------------------------
|
||||
|
||||
public override func formFieldValue() -> AnyHashable? {
|
||||
guard !options.isEmpty,
|
||||
let index = selectedIndex
|
||||
else { return nil }
|
||||
let index = selectedIndex
|
||||
else { return nil }
|
||||
|
||||
return options[index]
|
||||
}
|
||||
@ -34,7 +36,7 @@
|
||||
case options
|
||||
case selectedIndex
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
@ -47,10 +49,7 @@
|
||||
|
||||
if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) {
|
||||
self.selectedIndex = selectedIndex
|
||||
}
|
||||
|
||||
if let index = selectedIndex {
|
||||
baseValue = options.indices.contains(index) ? options[index] : nil
|
||||
baseValue = options.indices.contains(selectedIndex) ? options[selectedIndex] : nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -36,7 +36,7 @@ import UIKit
|
||||
label.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
return label
|
||||
}()
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Delegate
|
||||
//--------------------------------------------------
|
||||
@ -58,23 +58,23 @@ import UIKit
|
||||
|
||||
/// Toggles enabled (original) or disabled UI.
|
||||
public var isEnabled: Bool {
|
||||
get { return entryFieldContainer.isEnabled }
|
||||
get { entryFieldContainer.isEnabled }
|
||||
set (enabled) {
|
||||
self.titleLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
|
||||
self.feedbackLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
|
||||
self.entryFieldContainer.isEnabled = enabled
|
||||
self.entryFieldModel?.enabled = enabled
|
||||
titleLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
|
||||
feedbackLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
|
||||
entryFieldContainer.isEnabled = enabled
|
||||
entryFieldModel?.enabled = enabled
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggles error or original UI.
|
||||
public var showError: Bool {
|
||||
get { return entryFieldContainer.showError }
|
||||
get { entryFieldContainer.showError }
|
||||
set (error) {
|
||||
self.feedback = error ? errorMessage : entryFieldModel?.feedback
|
||||
self.feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack
|
||||
self.entryFieldContainer.showError = error
|
||||
self.entryFieldModel?.showError = error
|
||||
feedback = error ? errorMessage : entryFieldModel?.feedback
|
||||
feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack
|
||||
entryFieldContainer.showError = error
|
||||
entryFieldModel?.showError = error
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,49 +84,48 @@ import UIKit
|
||||
|
||||
/// Toggles original or locked UI.
|
||||
public var isLocked: Bool {
|
||||
get { return entryFieldContainer.isLocked }
|
||||
get { entryFieldContainer.isLocked }
|
||||
set (locked) {
|
||||
self.entryFieldContainer.isLocked = locked
|
||||
self.entryFieldModel?.locked = locked
|
||||
entryFieldContainer.isLocked = locked
|
||||
entryFieldModel?.locked = locked
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggles selected or original (unselected) UI.
|
||||
public var isSelected: Bool {
|
||||
get { return entryFieldContainer.isSelected }
|
||||
get { entryFieldContainer.isSelected }
|
||||
set (selected) {
|
||||
self.entryFieldContainer.isSelected = selected
|
||||
self.entryFieldModel?.selected = selected
|
||||
entryFieldContainer.isSelected = selected
|
||||
entryFieldModel?.selected = selected
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the text of titleLabel
|
||||
public var title: String? {
|
||||
get { return titleLabel.text }
|
||||
set (newText) {
|
||||
titleLabel.text = newText
|
||||
setAccessibilityString(newText)
|
||||
get { titleLabel.text }
|
||||
set {
|
||||
titleLabel.text = newValue
|
||||
setAccessibilityString(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Override this to conveniently get/set the textfield(s).
|
||||
public var text: String? {
|
||||
get { return nil }
|
||||
get { nil }
|
||||
set { fatalError("You MUST override EntryField's 'text' variable in your subclass.") }
|
||||
}
|
||||
|
||||
/// Sets feedback text in the textField.
|
||||
public var feedback: String? {
|
||||
get { return feedbackLabel.text }
|
||||
set (newFeedback) {
|
||||
feedbackLabel.text = newFeedback
|
||||
get { feedbackLabel.text }
|
||||
set {
|
||||
feedbackLabel.text = newValue
|
||||
feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true
|
||||
entryFieldContainer.refreshUI(updateMoleculeLayout: true)
|
||||
}
|
||||
}
|
||||
|
||||
public var entryFieldModel: EntryFieldModel? {
|
||||
return model as? EntryFieldModel
|
||||
model as? EntryFieldModel
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -223,7 +222,7 @@ import UIKit
|
||||
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) {
|
||||
// To Be Overriden
|
||||
}
|
||||
@ -345,9 +344,7 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return 115
|
||||
}
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 115 }
|
||||
}
|
||||
|
||||
// MARK: - Accessibility
|
||||
|
||||
@ -14,9 +14,7 @@ import Foundation
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public class var identifier: String {
|
||||
return ""
|
||||
}
|
||||
public class var identifier: String { "" }
|
||||
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
@ -48,7 +46,7 @@ import Foundation
|
||||
|
||||
/// Temporary binding mechanism for the view to update on enable changes.
|
||||
public var updateUI: ActionBlock?
|
||||
|
||||
|
||||
// TODO: Remove once updateUI is fixed with isSelected
|
||||
public var updateUIDynamicError: ActionBlock?
|
||||
|
||||
@ -142,9 +140,9 @@ import Foundation
|
||||
try container.encodeIfPresent(selected, forKey: .selected)
|
||||
try container.encodeIfPresent(errorTextColor, forKey: .errorTextColor)
|
||||
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(groupName, forKey: .groupName)
|
||||
try container.encode(enabled, forKey: .enabled)
|
||||
try container.encode(hideBorders, forKey: .hideBorders)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ import MVMCore
|
||||
|
||||
/// If you're using a MFViewController, you must set this to it
|
||||
public override weak var uiTextFieldDelegate: UITextFieldDelegate? {
|
||||
get { return textField.delegate }
|
||||
get { textField.delegate }
|
||||
set {
|
||||
textField.delegate = self
|
||||
proprietorTextDelegate = newValue
|
||||
@ -44,7 +44,7 @@ import MVMCore
|
||||
|
||||
/// Formats the MDN when setting and removes format of MDN when reading.
|
||||
public var mdn: String? {
|
||||
get { return MVMCoreUIUtility.removeMdnFormat(text) }
|
||||
get { MVMCoreUIUtility.removeMdnFormat(text) }
|
||||
set { text = MVMCoreUIUtility.formatMdn(newValue) }
|
||||
}
|
||||
|
||||
@ -160,9 +160,9 @@ import MVMCore
|
||||
|
||||
// Sometimes user add extra 1 in front of mdn in their address book
|
||||
if isNationalMDN,
|
||||
let unformedMDN = unformattedMDN,
|
||||
unformedMDN.count == 11,
|
||||
unformedMDN[(unformedMDN.index(unformedMDN.startIndex, offsetBy: 0))] == "1" {
|
||||
let unformedMDN = unformattedMDN,
|
||||
unformedMDN.count == 11,
|
||||
unformedMDN[(unformedMDN.index(unformedMDN.startIndex, offsetBy: 0))] == "1" {
|
||||
|
||||
let startIndex = unformedMDN.index(unformedMDN.startIndex, offsetBy: 1)
|
||||
unformattedMDN = String(unformedMDN[startIndex...])
|
||||
@ -211,17 +211,14 @@ import MVMCore
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
|
||||
proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool {
|
||||
|
||||
return proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true
|
||||
proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,5 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "mdnEntryField"
|
||||
}
|
||||
public override class var identifier: String { "mdnEntryField" }
|
||||
}
|
||||
|
||||
@ -54,16 +54,14 @@ import UIKit
|
||||
/// Validate when user resigns editing. Default: true
|
||||
public var validateWhenDoneEditing: Bool = true
|
||||
|
||||
public var textEntryFieldModel: TextEntryFieldModel? {
|
||||
return model as? TextEntryFieldModel
|
||||
}
|
||||
public var textEntryFieldModel: TextEntryFieldModel? { model as? TextEntryFieldModel }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Computed Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override var isEnabled: Bool {
|
||||
get { return super.isEnabled }
|
||||
get { super.isEnabled }
|
||||
set (enabled) {
|
||||
super.isEnabled = enabled
|
||||
|
||||
@ -77,7 +75,7 @@ import UIKit
|
||||
}
|
||||
|
||||
public override var showError: Bool {
|
||||
get { return super.showError }
|
||||
get { super.showError }
|
||||
set (error) {
|
||||
|
||||
if error {
|
||||
@ -96,16 +94,16 @@ import UIKit
|
||||
|
||||
/// The text of this TextField.
|
||||
open override var text: String? {
|
||||
get { return textField.text }
|
||||
get { textField.text }
|
||||
set {
|
||||
textField.text = newValue
|
||||
textEntryFieldModel?.text = newValue
|
||||
textField.text = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Placeholder access for the TextField.
|
||||
public var placeholder: String? {
|
||||
get { return textField.placeholder }
|
||||
get { textField.placeholder }
|
||||
set { textField.placeholder = newValue }
|
||||
}
|
||||
|
||||
@ -133,7 +131,7 @@ import UIKit
|
||||
|
||||
/// If you're using a ViewController, you must set this to it
|
||||
public weak var uiTextFieldDelegate: UITextFieldDelegate? {
|
||||
get { return textField.delegate }
|
||||
get { textField.delegate }
|
||||
set { textField.delegate = newValue }
|
||||
}
|
||||
|
||||
@ -222,9 +220,7 @@ import UIKit
|
||||
|
||||
@discardableResult
|
||||
@objc override open func resignFirstResponder() -> Bool {
|
||||
if validateWhenDoneEditing {
|
||||
validateText()
|
||||
}
|
||||
if validateWhenDoneEditing { validateText() }
|
||||
textField.resignFirstResponder()
|
||||
isSelected = false
|
||||
return true
|
||||
@ -239,6 +235,11 @@ import UIKit
|
||||
/// Executes on UITextField.textDidBeginEditingNotification
|
||||
@objc override func startEditing() {
|
||||
super.startEditing()
|
||||
|
||||
if textEntryFieldModel?.clearTextOnTap ?? false {
|
||||
text = ""
|
||||
}
|
||||
|
||||
textField.becomeFirstResponder()
|
||||
}
|
||||
|
||||
@ -257,14 +258,33 @@ import UIKit
|
||||
showError = false
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if let isValid = textEntryFieldModel?.isValid {
|
||||
self.isValid = isValid
|
||||
}
|
||||
|
||||
regexTextFieldOutputIfAvailable()
|
||||
|
||||
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?) {
|
||||
resignFirstResponder()
|
||||
}
|
||||
@ -317,6 +337,10 @@ import UIKit
|
||||
case .password, .secure:
|
||||
textField.isSecureTextEntry = true
|
||||
|
||||
case .numberSecure:
|
||||
textField.isSecureTextEntry = true
|
||||
textField.keyboardType = .numberPad
|
||||
|
||||
case .number:
|
||||
textField.keyboardType = .numberPad
|
||||
|
||||
@ -326,17 +350,20 @@ import UIKit
|
||||
case .phone:
|
||||
textField.keyboardType = .phonePad
|
||||
|
||||
default:
|
||||
break
|
||||
default: break
|
||||
}
|
||||
|
||||
// Override the preset keyboard set in type.
|
||||
if let keyboardType = model.assignKeyboardType() {
|
||||
textField.keyboardType = keyboardType
|
||||
}
|
||||
|
||||
textField.accessibilityIdentifier = model.accessibilityIdentifier
|
||||
uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate
|
||||
observingTextFieldDelegate = delegateObject?.observingTextFieldDelegate
|
||||
setupTextFieldToolbar()
|
||||
|
||||
if isSelected {
|
||||
startEditing()
|
||||
}
|
||||
if isSelected { startEditing() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
case password
|
||||
case secure
|
||||
case number
|
||||
case numberSecure
|
||||
case email
|
||||
case text
|
||||
case phone
|
||||
@ -25,15 +26,69 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "textField"
|
||||
}
|
||||
public override class var identifier: String { "textField" }
|
||||
|
||||
public var placeholder: String?
|
||||
public var enabledTextColor: Color = Color(uiColor: .mvmBlack)
|
||||
public var disabledTextColor: Color = Color(uiColor: .mvmCoolGray3)
|
||||
public var textAlignment: NSTextAlignment = .left
|
||||
public var keyboardOverride: String?
|
||||
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
|
||||
@ -44,7 +99,11 @@
|
||||
case textAlignment
|
||||
case enabledTextColor
|
||||
case disabledTextColor
|
||||
case keyboardOverride
|
||||
case type
|
||||
case clearTextOnTap
|
||||
case displayFormat
|
||||
case displayMask
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -56,8 +115,15 @@
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
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)
|
||||
|
||||
if let clearTextOnTap = try typeContainer.decodeIfPresent(Bool.self, forKey: .clearTextOnTap) {
|
||||
self.clearTextOnTap = clearTextOnTap
|
||||
}
|
||||
|
||||
if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor) {
|
||||
self.enabledTextColor = enabledTextColor
|
||||
}
|
||||
@ -76,8 +142,12 @@
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(placeholder, forKey: .placeholder)
|
||||
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(disabledTextColor, forKey: .disabledTextColor)
|
||||
try container.encodeIfPresent(type, forKey: .type)
|
||||
try container.encode(clearTextOnTap, forKey: .clearTextOnTap)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,11 +31,11 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
//--------------------------------------------------
|
||||
|
||||
public var textViewEntryFieldModel: TextViewEntryFieldModel? {
|
||||
return model as? TextViewEntryFieldModel
|
||||
model as? TextViewEntryFieldModel
|
||||
}
|
||||
|
||||
public override var isEnabled: Bool {
|
||||
get { return super.isEnabled }
|
||||
get { super.isEnabled }
|
||||
set (enabled) {
|
||||
super.isEnabled = enabled
|
||||
|
||||
@ -53,7 +53,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
}
|
||||
|
||||
public override var showError: Bool {
|
||||
get { return super.showError }
|
||||
get { super.showError }
|
||||
set (error) {
|
||||
|
||||
if error {
|
||||
@ -68,7 +68,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
|
||||
/// The text of this textView.
|
||||
open override var text: String? {
|
||||
get { return textViewEntryFieldModel?.text }
|
||||
get { textViewEntryFieldModel?.text }
|
||||
set {
|
||||
textView.text = newValue
|
||||
textViewEntryFieldModel?.text = newValue
|
||||
@ -77,7 +77,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
|
||||
/// Placeholder access for the textView.
|
||||
public var placeholder: String? {
|
||||
get { return textViewEntryFieldModel?.placeholder }
|
||||
get { textViewEntryFieldModel?.placeholder }
|
||||
set {
|
||||
textView.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
|
||||
public weak var uiTextViewDelegate: UITextViewDelegate? {
|
||||
get { return textView.delegate }
|
||||
get { textView.delegate }
|
||||
set { textView.delegate = newValue }
|
||||
}
|
||||
|
||||
@ -149,17 +149,17 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
@objc open override func setupFieldContainerContent(_ container: UIView) {
|
||||
|
||||
container.addSubview(textView)
|
||||
|
||||
|
||||
topConstraint = textView.topAnchor.constraint(equalTo: container.topAnchor, constant: Padding.Three)
|
||||
leadingConstraint = textView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: Padding.Three)
|
||||
trailingConstraint = container.trailingAnchor.constraint(equalTo: textView.trailingAnchor, constant: Padding.Three)
|
||||
bottomConstraint = container.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: Padding.Three)
|
||||
|
||||
|
||||
topConstraint?.isActive = true
|
||||
leadingConstraint?.isActive = true
|
||||
trailingConstraint?.isActive = true
|
||||
bottomConstraint?.isActive = true
|
||||
|
||||
|
||||
heightConstraint = textView.heightAnchor.constraint(equalToConstant: 0)
|
||||
accessibilityElements = [titleLabel, textView, feedbackLabel]
|
||||
}
|
||||
@ -203,7 +203,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
/// Executes on UITextView.textDidEndEditingNotification
|
||||
@objc override func endInputing() {
|
||||
super.endInputing()
|
||||
|
||||
|
||||
// Don't show error till user starts typing.
|
||||
guard text?.count ?? 0 != 0 else {
|
||||
showError = false
|
||||
@ -241,6 +241,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
|
||||
textView.isEditable = model.editable
|
||||
textView.textAlignment = model.textAlignment
|
||||
textView.accessibilityIdentifier = model.accessibilityIdentifier
|
||||
textView.textColor = model.enabled ? model.enabledTextColor.uiColor : model.disabledTextColor.uiColor
|
||||
textView.font = model.fontStyle.getFont()
|
||||
textView.placeholder = model.placeholder ?? ""
|
||||
@ -252,13 +253,17 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele
|
||||
case .secure, .password:
|
||||
textView.isSecureTextEntry = true
|
||||
|
||||
case .numberSecure:
|
||||
textView.isSecureTextEntry = true
|
||||
textView.keyboardType = .numberPad
|
||||
|
||||
case .number:
|
||||
textView.keyboardType = .numberPad
|
||||
|
||||
case .email:
|
||||
textView.keyboardType = .emailAddress
|
||||
|
||||
default: break
|
||||
default: break
|
||||
}
|
||||
|
||||
/// No point in configuring if the TextView is Read-only.
|
||||
|
||||
@ -14,9 +14,7 @@ class TextViewEntryFieldModel: TextEntryFieldModel {
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "textView"
|
||||
}
|
||||
public override class var identifier: String { "textView" }
|
||||
|
||||
public var accessibilityText: String?
|
||||
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 editable: Bool = true
|
||||
public var showsPlaceholder: Bool = false
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -25,7 +25,7 @@ import MVMCore
|
||||
var delegateObject: MVMCoreUIDelegateObject?
|
||||
|
||||
public var checkboxModel: CheckboxModel? {
|
||||
return model as? CheckboxModel
|
||||
model as? CheckboxModel
|
||||
}
|
||||
|
||||
public static let defaultHeightWidth: CGFloat = 18.0
|
||||
@ -59,7 +59,7 @@ import MVMCore
|
||||
|
||||
/// Retrieves ideeal radius value to curve square into a circle.
|
||||
public var cornerRadiusValue: CGFloat {
|
||||
return bounds.size.height / 2
|
||||
bounds.size.height / 2
|
||||
}
|
||||
|
||||
/// Action Block called when the switch is selected.
|
||||
@ -102,23 +102,17 @@ import MVMCore
|
||||
|
||||
/// Color of the check mark.
|
||||
public var checkColor: UIColor = .mvmBlack {
|
||||
didSet {
|
||||
setShapeLayerStrokeColor(checkColor)
|
||||
}
|
||||
didSet { setShapeLayerStrokeColor(checkColor) }
|
||||
}
|
||||
|
||||
/// Border width of the checkbox
|
||||
public var borderWidth: CGFloat = 1 {
|
||||
didSet {
|
||||
layer.borderWidth = borderWidth
|
||||
}
|
||||
didSet { layer.borderWidth = borderWidth }
|
||||
}
|
||||
|
||||
/// border color of the Checkbox
|
||||
public var borderColor: UIColor = .mvmBlack {
|
||||
didSet {
|
||||
layer.borderColor = borderColor.cgColor
|
||||
}
|
||||
didSet { layer.borderColor = borderColor.cgColor }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,6 +353,7 @@ import MVMCore
|
||||
}
|
||||
|
||||
override open func accessibilityActivate() -> Bool {
|
||||
guard isEnabled else { return false }
|
||||
sendActions(for: .touchUpInside)
|
||||
return true
|
||||
}
|
||||
@ -367,9 +362,7 @@ import MVMCore
|
||||
// MARK: - Molecular
|
||||
//--------------------------------------------------
|
||||
|
||||
open func needsToBeConstrained() -> Bool {
|
||||
return true
|
||||
}
|
||||
open func needsToBeConstrained() -> Bool { true }
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
@ -396,11 +389,9 @@ import MVMCore
|
||||
}
|
||||
|
||||
private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
if let actionMap = actionModel.toJSON() {
|
||||
var additionalDatatoUpdate = additionalData ?? [:]
|
||||
additionalDatatoUpdate[KeySourceModel] = checkboxModel
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject)
|
||||
}
|
||||
var additionalDataToUpdate = additionalData ?? [:]
|
||||
additionalDataToUpdate[KeySourceModel] = checkboxModel
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataToUpdate, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
|
||||
@ -16,6 +16,7 @@ import Foundation
|
||||
|
||||
public static var identifier: String = "checkbox"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var checked: Bool = false
|
||||
public var enabled: Bool = true
|
||||
public var animated: Bool = true
|
||||
@ -44,6 +45,7 @@ import Foundation
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case accessibilityIdentifier
|
||||
case checked
|
||||
case enabled
|
||||
case inverted
|
||||
@ -69,9 +71,7 @@ import Foundation
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
return checked
|
||||
}
|
||||
public func formFieldValue() -> AnyHashable? { checked }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
@ -89,6 +89,8 @@ import Foundation
|
||||
required public init(from decoder: Decoder) throws {
|
||||
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) {
|
||||
self.borderWidth = borderWidth
|
||||
}
|
||||
@ -169,6 +171,7 @@ import Foundation
|
||||
try container.encode(borderWidth, forKey: .borderWidth)
|
||||
try container.encode(checked, forKey: .checked)
|
||||
try container.encode(inverted, forKey: .inverted)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encodeIfPresent(checkColor, forKey: .checkColor)
|
||||
try container.encodeIfPresent(invertedColor, forKey: .invertedColor)
|
||||
try container.encodeIfPresent(invertedBackgroundColor, forKey: .invertedBackgroundColor)
|
||||
|
||||
@ -71,6 +71,7 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func setupView() {
|
||||
super.setupView()
|
||||
addTarget(self, action: #selector(tapAction), for: .touchUpInside)
|
||||
|
||||
@ -6,15 +6,16 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "heart"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var isActive: Bool = false
|
||||
public var activeColor: Color = Color(uiColor: .mvmRed)
|
||||
public var inActiveColor: Color = Color(uiColor: .clear)
|
||||
@ -24,9 +25,11 @@ open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case isActive
|
||||
case activeColor
|
||||
case inActiveColor
|
||||
@ -43,13 +46,18 @@ open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
|
||||
if let isActive = try typeContainer.decodeIfPresent(Bool.self, forKey: .isActive) {
|
||||
self.isActive = isActive
|
||||
}
|
||||
|
||||
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) {
|
||||
self.activeColor = activeColor
|
||||
}
|
||||
|
||||
if let inActiveColor = try typeContainer.decodeIfPresent(Color.self, forKey: .inActiveColor) {
|
||||
self.inActiveColor = inActiveColor
|
||||
}
|
||||
|
||||
if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) {
|
||||
self.action = action
|
||||
}
|
||||
@ -61,6 +69,7 @@ open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(isActive, forKey: .isActive)
|
||||
try container.encode(activeColor, forKey: .activeColor)
|
||||
|
||||
@ -6,9 +6,12 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class RadioBox: Control, MFButtonProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public let label = Label(fontStyle: .RegularBodySmall)
|
||||
public let subTextLabel = Label(fontStyle: .RegularMicro)
|
||||
public var isOutOfStock = false
|
||||
@ -26,22 +29,20 @@ open class RadioBox: Control, MFButtonProtocol {
|
||||
var additionalData: [AnyHashable: Any]?
|
||||
|
||||
public var radioBoxModel: RadioBoxModel? {
|
||||
return model as? RadioBoxModel
|
||||
model as? RadioBoxModel
|
||||
}
|
||||
|
||||
public override var isSelected: Bool {
|
||||
didSet {
|
||||
updateAccessibility()
|
||||
}
|
||||
didSet { updateAccessibility() }
|
||||
}
|
||||
|
||||
public override var isEnabled: Bool {
|
||||
didSet {
|
||||
updateAccessibility()
|
||||
}
|
||||
didSet { updateAccessibility() }
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
@ -75,8 +76,6 @@ open class RadioBox: Control, MFButtonProtocol {
|
||||
isAccessibilityElement = true
|
||||
}
|
||||
|
||||
// MARK: - MoleculeViewProtocol
|
||||
|
||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
guard let model = model as? RadioBoxModel else { return }
|
||||
@ -99,7 +98,9 @@ open class RadioBox: Control, MFButtonProtocol {
|
||||
accentColor = .mvmRed
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - State Handling
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func draw(_ layer: CALayer, in ctx: CGContext) {
|
||||
// Draw the strikethrough
|
||||
@ -213,21 +214,29 @@ open class RadioBox: Control, MFButtonProtocol {
|
||||
return mask
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Accessibility
|
||||
//--------------------------------------------------
|
||||
|
||||
public func updateAccessibility() {
|
||||
|
||||
var message = ""
|
||||
|
||||
if let labelText = label.text, label.hasText {
|
||||
message += labelText + ", "
|
||||
}
|
||||
|
||||
if let subLabelText = subTextLabel.text, subTextLabel.hasText {
|
||||
message += subLabelText + ", "
|
||||
}
|
||||
accessibilityLabel = message
|
||||
|
||||
accessibilityLabel = message
|
||||
accessibilityTraits = .button
|
||||
|
||||
if isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
}
|
||||
|
||||
if !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
}
|
||||
|
||||
@ -6,10 +6,17 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
open class RadioBoxCollectionViewCell: CollectionViewCell {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Outlets
|
||||
//--------------------------------------------------
|
||||
|
||||
public let radioBox = RadioBox()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
backgroundColor = .clear
|
||||
|
||||
@ -6,12 +6,16 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@objcMembers public class RadioBoxModel: MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "radioBox"
|
||||
public var text: String
|
||||
public var subText: String?
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var selectedAccentColor: Color?
|
||||
public var selected: Bool = false
|
||||
public var enabled: Bool = true
|
||||
@ -19,12 +23,17 @@ import Foundation
|
||||
public var fieldValue: String?
|
||||
public var action: ActionModelProtocol?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case text
|
||||
case subText
|
||||
case selectedAccentColor
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case selected
|
||||
case enabled
|
||||
case strikethrough
|
||||
@ -32,18 +41,26 @@ import Foundation
|
||||
case action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
text = try typeContainer.decode(String.self, forKey: .text)
|
||||
subText = try typeContainer.decodeIfPresent(String.self, forKey: .subText)
|
||||
selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor)
|
||||
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) {
|
||||
selected = isSelected
|
||||
}
|
||||
|
||||
if let isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
|
||||
enabled = isEnabled
|
||||
}
|
||||
|
||||
if let isStrikeTrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) {
|
||||
strikethrough = isStrikeTrough
|
||||
}
|
||||
@ -59,6 +76,7 @@ import Foundation
|
||||
try container.encodeIfPresent(subText, forKey: .subText)
|
||||
try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(selected, forKey: .selected)
|
||||
try container.encode(enabled, forKey: .enabled)
|
||||
try container.encode(strikethrough, forKey: .strikethrough)
|
||||
|
||||
@ -57,11 +57,12 @@ open class RadioBoxes: View {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
|
||||
guard let radioBoxesModel = model as? RadioBoxesModel else { return }
|
||||
boxes = radioBoxesModel.boxes
|
||||
FormValidator.setupValidation(for: radioBoxesModel, delegate: delegateObject?.formHolderDelegate)
|
||||
guard let model = model as? RadioBoxesModel else { return }
|
||||
boxes = model.boxes
|
||||
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
|
||||
|
||||
backgroundColor = radioBoxesModel.backgroundColor?.uiColor
|
||||
backgroundColor = model.backgroundColor?.uiColor
|
||||
|
||||
registerCells()
|
||||
setHeight()
|
||||
collectionView.reloadData()
|
||||
@ -168,4 +169,3 @@ extension RadioBoxes: UICollectionViewDelegate {
|
||||
cell.updateAccessibility()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,17 +6,25 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@objcMembers public class RadioBoxesModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "radioBoxes"
|
||||
public var boxes: [RadioBoxModel]
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var selectedAccentColor: Color?
|
||||
public var boxesColor: Color?
|
||||
public var fieldKey: String?
|
||||
public var groupName: String = FormValidator.defaultGroupName
|
||||
public var baseValue: AnyHashable?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Returns the fieldValue of the selected box, otherwise the text of the selected box.
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
let selectedBox = boxes.first { (box) -> Bool in
|
||||
@ -25,20 +33,30 @@ import Foundation
|
||||
return selectedBox?.fieldValue ?? selectedBox?.text
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case selectedAccentColor
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case boxesColor
|
||||
case boxes
|
||||
case fieldKey
|
||||
case groupName
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
boxesColor = try typeContainer.decodeIfPresent(Color.self, forKey: .boxesColor)
|
||||
boxes = try typeContainer.decode([RadioBoxModel].self, forKey: .boxes)
|
||||
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
|
||||
@ -54,6 +72,7 @@ import Foundation
|
||||
try container.encode(boxes, forKey: .boxes)
|
||||
try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
|
||||
try container.encode(groupName, forKey: .groupName)
|
||||
}
|
||||
|
||||
@ -15,9 +15,7 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
|
||||
public var diameter: CGFloat = 30 {
|
||||
didSet {
|
||||
widthConstraint?.constant = diameter
|
||||
}
|
||||
didSet { widthConstraint?.constant = diameter }
|
||||
}
|
||||
|
||||
@objc public override var isSelected: Bool {
|
||||
@ -33,12 +31,10 @@ import UIKit
|
||||
var additionalData: [AnyHashable: Any]?
|
||||
|
||||
public var radioModel: RadioButtonModel? {
|
||||
return model as? RadioButtonModel
|
||||
model as? RadioButtonModel
|
||||
}
|
||||
|
||||
lazy public var radioGroupName: String? = {
|
||||
return radioModel?.fieldKey
|
||||
}()
|
||||
lazy public var radioGroupName: String? = { radioModel?.fieldKey }()
|
||||
|
||||
lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = {
|
||||
|
||||
@ -95,33 +91,34 @@ import UIKit
|
||||
if !isEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
let wasPreviouslySelected = isSelected
|
||||
if let radioButtonModel = radioButtonSelectionHelper {
|
||||
radioButtonModel.selected(self)
|
||||
} else {
|
||||
isSelected = !isSelected
|
||||
}
|
||||
|
||||
if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected {
|
||||
Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel)
|
||||
}
|
||||
|
||||
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
|
||||
setNeedsDisplay()
|
||||
}
|
||||
|
||||
public func isValidField() -> Bool {
|
||||
return isSelected
|
||||
}
|
||||
public func isValidField() -> Bool { isSelected }
|
||||
|
||||
public func formFieldName() -> String? {
|
||||
return radioModel?.fieldKey
|
||||
radioModel?.fieldKey
|
||||
}
|
||||
|
||||
public func formFieldGroupName() -> String? {
|
||||
return radioModel?.fieldKey
|
||||
radioModel?.fieldKey
|
||||
}
|
||||
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
return radioModel?.fieldValue
|
||||
radioModel?.fieldValue
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MVMCore
|
||||
|
||||
|
||||
@ -17,6 +16,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
|
||||
public static var identifier: String = "radioButton"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var state: Bool = false
|
||||
public var enabled: Bool = true
|
||||
|
||||
@ -35,6 +35,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case state
|
||||
case enabled
|
||||
case fieldValue
|
||||
@ -56,9 +57,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
// MARK: - Validation
|
||||
//--------------------------------------------------
|
||||
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
return fieldValue
|
||||
}
|
||||
public func formFieldValue() -> AnyHashable? { fieldValue }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
@ -76,6 +75,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
}
|
||||
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
|
||||
baseValue = state
|
||||
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
|
||||
@ -89,6 +89,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(state, forKey: .state)
|
||||
try container.encode(enabled, forKey: .enabled)
|
||||
|
||||
@ -6,8 +6,6 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@objcMembers public class RadioButtonSelectionHelper: FormFieldProtocol {
|
||||
//--------------------------------------------------
|
||||
@ -77,7 +75,5 @@ import Foundation
|
||||
// MARK: - FormValidationFormFieldProtocol
|
||||
extension RadioButtonSelectionHelper {
|
||||
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
return selectedRadioButtonModel?.fieldValue
|
||||
}
|
||||
public func formFieldValue() -> AnyHashable? { selectedRadioButtonModel?.fieldValue }
|
||||
}
|
||||
|
||||
@ -77,6 +77,7 @@ open class RadioSwatch: Control, MFButtonProtocol {
|
||||
//------------------------------------------------------
|
||||
// MARK: - State Handling
|
||||
//------------------------------------------------------
|
||||
|
||||
open override func draw(_ layer: CALayer, in ctx: CGContext) {
|
||||
//Draw the swatch
|
||||
circleLayer?.removeFromSuperlayer()
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class RadioSwatchCollectionViewCell: CollectionViewCell {
|
||||
public let radioSwatch = RadioSwatch()
|
||||
|
||||
|
||||
@ -6,11 +6,15 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class RadioSwatchModel: MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "radioSwatch"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var color: Color = Color(uiColor: .mvmBlue)
|
||||
public var text: String?
|
||||
public var selected: Bool = false
|
||||
@ -19,9 +23,14 @@ import Foundation
|
||||
public var fieldValue: String?
|
||||
public var action: ActionModelProtocol?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case color
|
||||
case text
|
||||
case selected
|
||||
@ -31,22 +40,33 @@ import Foundation
|
||||
case action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
|
||||
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) {
|
||||
self.color = color
|
||||
}
|
||||
|
||||
text = try typeContainer.decodeIfPresent(String.self, forKey: .text)
|
||||
|
||||
if let selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) {
|
||||
self.selected = selected
|
||||
}
|
||||
|
||||
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
|
||||
self.enabled = enabled
|
||||
}
|
||||
|
||||
if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) {
|
||||
self.strikethrough = strikethrough
|
||||
}
|
||||
|
||||
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
|
||||
}
|
||||
@ -55,6 +75,7 @@ import Foundation
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(color, forKey: .color)
|
||||
try container.encodeIfPresent(text, forKey: .text)
|
||||
try container.encode(selected, forKey: .selected)
|
||||
@ -64,5 +85,3 @@ import Foundation
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -55,9 +55,9 @@ open class RadioSwatches: View {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
|
||||
guard let radioSwatchesModel = model as? RadioSwatchesModel else { return }
|
||||
swatches = radioSwatchesModel.swatches
|
||||
FormValidator.setupValidation(for: radioSwatchesModel, delegate: delegateObject?.formHolderDelegate)
|
||||
guard let model = model as? RadioSwatchesModel else { return }
|
||||
swatches = model.swatches
|
||||
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
|
||||
collectionView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
@ -6,16 +6,24 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class RadioSwatchesModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "radioSwatches"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var swatches: [RadioSwatchModel]
|
||||
public var fieldKey: String?
|
||||
public var groupName: String = FormValidator.defaultGroupName
|
||||
public var baseValue: AnyHashable?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Returns the fieldValue of the selected swatch, otherwise the text of selected swatch.
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
let selectedSwatch = swatches.first { (swatch) -> Bool in
|
||||
@ -24,17 +32,27 @@ import Foundation
|
||||
return selectedSwatch?.fieldValue ?? selectedSwatch?.text
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case swatches
|
||||
case fieldKey
|
||||
case groupName
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
swatches = try typeContainer.decode([RadioSwatchModel].self, forKey: .swatches)
|
||||
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
|
||||
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
|
||||
@ -47,6 +65,7 @@ import Foundation
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(swatches, forKey: .swatches)
|
||||
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
|
||||
try container.encode(groupName, forKey: .groupName)
|
||||
|
||||
@ -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.
|
||||
public var shouldToggleAction: ActionBlockConfirmation? = {
|
||||
return { return true }
|
||||
return { true }
|
||||
}()
|
||||
|
||||
// Sizes are from InVision design specs.
|
||||
@ -69,9 +69,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
|
||||
|
||||
/// Simple means to prevent user interaction with the toggle.
|
||||
public var isLocked: Bool = false {
|
||||
didSet {
|
||||
isUserInteractionEnabled = !isLocked
|
||||
}
|
||||
didSet { isUserInteractionEnabled = !isLocked }
|
||||
}
|
||||
|
||||
/// The state on the toggle. Default value: false.
|
||||
@ -109,7 +107,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
|
||||
}
|
||||
|
||||
public var toggleModel: ToggleModel? {
|
||||
return model as? ToggleModel
|
||||
model as? ToggleModel
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -392,21 +390,17 @@ public typealias ActionBlockConfirmation = () -> (Bool)
|
||||
accessibilityLabel = accessibileString
|
||||
}
|
||||
|
||||
let actionMap = model.action?.toJSON()
|
||||
let alternateActionMap = model.alternateAction?.toJSON()
|
||||
let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model)
|
||||
if actionMap != nil || alternateActionMap != nil {
|
||||
if model.action != nil || model.alternateAction != nil {
|
||||
didToggleAction = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if self.isOn {
|
||||
if actionMap != nil {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
if let action = model.action {
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
}
|
||||
} else {
|
||||
if alternateActionMap != nil {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
} else if actionMap != nil {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
if let action = model.alternateAction ?? model.action {
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -414,18 +408,14 @@ public typealias ActionBlockConfirmation = () -> (Bool)
|
||||
}
|
||||
|
||||
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return Self.getContainerHeight()
|
||||
Self.getContainerHeight()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||
extension Toggle {
|
||||
|
||||
public func needsToBeConstrained() -> Bool {
|
||||
return true
|
||||
}
|
||||
public func needsToBeConstrained() -> Bool { true }
|
||||
|
||||
public func horizontalAlignment() -> UIStackView.Alignment {
|
||||
return .trailing
|
||||
}
|
||||
public func horizontalAlignment() -> UIStackView.Alignment { .trailing }
|
||||
}
|
||||
@ -6,8 +6,6 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol {
|
||||
//--------------------------------------------------
|
||||
@ -15,6 +13,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "toggle"
|
||||
public var accessibilityIdentifier: String?
|
||||
public var backgroundColor: Color?
|
||||
public var state: Bool = false
|
||||
public var animated: Bool = true
|
||||
@ -42,6 +41,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
|
||||
case enabled
|
||||
case action
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case alternateAction
|
||||
case accessibilityText
|
||||
case onTintColor
|
||||
@ -56,9 +56,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
return state
|
||||
}
|
||||
public func formFieldValue() -> AnyHashable? { state }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
@ -91,6 +89,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
|
||||
alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction)
|
||||
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) {
|
||||
self.onTintColor = onTintColor
|
||||
@ -120,6 +119,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
@ -16,6 +16,7 @@ import Foundation
|
||||
|
||||
public static var identifier: String = "dashLine"
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public var dashColor: Color = Color(uiColor: .mvmCoolGray3)
|
||||
public var dashColor_inverted: Color = Color(uiColor: .mvmWhite)
|
||||
public var isHidden: Bool = false
|
||||
@ -36,6 +37,7 @@ import Foundation
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case dashColor_inverted
|
||||
case dashColor
|
||||
case isHidden
|
||||
@ -57,6 +59,7 @@ import Foundation
|
||||
}
|
||||
|
||||
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) {
|
||||
self.isHidden = isHidden
|
||||
@ -69,5 +72,6 @@ import Foundation
|
||||
try container.encode(dashColor, forKey: .dashColor)
|
||||
try container.encode(isHidden, forKey: .isHidden)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public typealias ActionBlock = () -> ()
|
||||
}
|
||||
|
||||
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
|
||||
@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:
|
||||
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()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionAtt.action, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
addActionAttributes(range: range, string: attributedString)
|
||||
|
||||
@ -794,17 +792,11 @@ extension Label {
|
||||
accessibilityTraits = .staticText
|
||||
}
|
||||
|
||||
public func needsToBeConstrained() -> Bool {
|
||||
return true
|
||||
}
|
||||
public func needsToBeConstrained() -> Bool { true }
|
||||
|
||||
public func horizontalAlignment() -> UIStackView.Alignment {
|
||||
return .leading
|
||||
}
|
||||
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
|
||||
|
||||
public func copyBackgroundColor() -> Bool {
|
||||
return true
|
||||
}
|
||||
public func copyBackgroundColor() -> Bool { true }
|
||||
}
|
||||
|
||||
// MARK: - Multi-Link Functionality
|
||||
@ -916,6 +908,7 @@ extension UITapGestureRecognizer {
|
||||
if label.makeWholeViewClickable {
|
||||
return true
|
||||
}
|
||||
|
||||
guard let abstractContainer = label.abstractTextContainer() else { return false }
|
||||
let textContainer = abstractContainer.0
|
||||
let layoutManager = abstractContainer.1
|
||||
@ -990,6 +983,7 @@ extension Label {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,9 +12,7 @@ open class LabelAttributeActionModel: LabelAttributeModel {
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "action"
|
||||
}
|
||||
override public class var identifier: String { "action" }
|
||||
|
||||
var action: ActionModelProtocol
|
||||
|
||||
|
||||
@ -12,9 +12,7 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "color"
|
||||
}
|
||||
override public class var identifier: String { "color" }
|
||||
|
||||
var textColor: Color?
|
||||
|
||||
|
||||
@ -12,9 +12,7 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "font"
|
||||
}
|
||||
override public class var identifier: String { "font" }
|
||||
|
||||
var style: Styler.Font?
|
||||
var name: String?
|
||||
|
||||
@ -12,9 +12,7 @@ class LabelAttributeImageModel: LabelAttributeModel {
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "image"
|
||||
}
|
||||
override public class var identifier: String { "image" }
|
||||
|
||||
var size: CGFloat?
|
||||
var name: String?
|
||||
|
||||
@ -12,20 +12,14 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var categoryName: String {
|
||||
return "\(LabelAttributeModel.self)"
|
||||
}
|
||||
public static var categoryName: String { "\(LabelAttributeModel.self)" }
|
||||
|
||||
public static var categoryCodingKey: String {
|
||||
return "type"
|
||||
}
|
||||
public static var categoryCodingKey: String { "type" }
|
||||
|
||||
public class var identifier: String {
|
||||
return ""
|
||||
}
|
||||
public class var identifier: String { "" }
|
||||
|
||||
var type: String {
|
||||
get { return Self.identifier }
|
||||
get { Self.identifier }
|
||||
}
|
||||
|
||||
var location: Int
|
||||
|
||||
@ -12,9 +12,7 @@
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "strikethrough"
|
||||
}
|
||||
override public class var identifier: String { "strikethrough" }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
|
||||
@ -14,9 +14,7 @@ import UIKit
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "underline"
|
||||
}
|
||||
override public class var identifier: String { "underline" }
|
||||
|
||||
/// 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.
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
|
||||
public var addSizeConstraintsForAspectRatio = 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.
|
||||
var imageWidth: CGFloat?
|
||||
var imageHeight: CGFloat?
|
||||
@ -228,13 +229,13 @@
|
||||
|
||||
let widthWillChange = !MVMCoreGetterUtility.cgfequal(widthConstraint?.constant ?? 0, width ?? 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
|
||||
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.
|
||||
func addConstraints(width: NSNumber?, height: NSNumber?, size: CGSize?) {
|
||||
func addConstraints(width: CGFloat?, height: CGFloat?, size: CGSize?) {
|
||||
|
||||
widthConstraint?.isActive = false
|
||||
heightConstraint?.isActive = false
|
||||
@ -242,15 +243,15 @@
|
||||
guard addSizeConstraintsForAspectRatio else { return }
|
||||
|
||||
if let width = width, let height = height {
|
||||
setHeight(height.cgfloat())
|
||||
setWidth(width.cgfloat())
|
||||
setHeight(height)
|
||||
setWidth(width)
|
||||
} 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?.priority = UILayoutPriority(rawValue: 900)
|
||||
heightConstraint?.isActive = true
|
||||
} 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?.priority = UILayoutPriority(rawValue: 900)
|
||||
widthConstraint?.isActive = true
|
||||
@ -288,7 +289,7 @@
|
||||
if shouldLoadImage(withName: imageModel.image, width: width, height: height) {
|
||||
imageView.image = 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 {
|
||||
@ -309,13 +310,13 @@
|
||||
// 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()
|
||||
MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in
|
||||
self.currentImageName = imageName
|
||||
self.currentImageWidth = width?.cgfloat()
|
||||
self.currentImageHeight = height?.cgfloat()
|
||||
self.currentImageWidth = width
|
||||
self.currentImageHeight = height
|
||||
if MVMCoreCache.isHostedImage(imageName) {
|
||||
self.loadingSpinner.resumeSpinnerAfterDelay()
|
||||
self.loadingSpinnerHeightConstraint?.constant = self.spinnerHeight
|
||||
@ -338,9 +339,9 @@
|
||||
let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback")
|
||||
if let format = format, format.lowercased().contains("gif") {
|
||||
// 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 {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ import UIKit
|
||||
private let stack = Stack<StackModel>()
|
||||
|
||||
var multiProgressModel: MultiProgressBarModel? {
|
||||
get { return model as? MultiProgressBarModel }
|
||||
get { model as? MultiProgressBarModel }
|
||||
}
|
||||
|
||||
var roundedCorners: Bool = false {
|
||||
@ -85,7 +85,7 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
|
||||
/// 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()
|
||||
|
||||
guard let stackModel = stack.stackModel else { return }
|
||||
@ -103,7 +103,6 @@ import UIKit
|
||||
stack.set(with: stackModel, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
|
||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
|
||||
@ -115,6 +114,6 @@ import UIKit
|
||||
}
|
||||
|
||||
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return (model as? MultiProgressBarModel)?.thickness ?? 8
|
||||
(model as? MultiProgressBarModel)?.thickness ?? 8
|
||||
}
|
||||
}
|
||||
|
||||
108
MVMCoreUI/Atomic/Atoms/Views/Video.swift
Normal file
108
MVMCoreUI/Atomic/Atoms/Views/Video.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
141
MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift
Normal file
141
MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
167
MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift
Normal file
167
MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
@ -79,6 +79,7 @@ import Foundation
|
||||
MoleculeObjectMapping.shared()?.register(viewClass: DigitEntryField.self, viewModelClass: DigitEntryFieldModel.self)
|
||||
MoleculeObjectMapping.shared()?.register(viewClass: ItemDropdownEntryField.self, viewModelClass: ItemDropdownEntryFieldModel.self)
|
||||
MoleculeObjectMapping.shared()?.register(viewClass: DateDropdownEntryField.self, viewModelClass: DateDropdownEntryFieldModel.self)
|
||||
MoleculeObjectMapping.shared()?.register(viewClass: MultiItemDropdownEntryField.self, viewModelClass: MultiItemDropdownEntryFieldModel.self)
|
||||
|
||||
// MARK:- Selectors
|
||||
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: WebView.self, viewModelClass: WebViewModel.self)
|
||||
MoleculeObjectMapping.shared()?.register(viewClass: LoadingSpinner.self, viewModelClass: LoadingSpinnerModel.self)
|
||||
MoleculeObjectMapping.shared()?.register(viewClass: Video.self, viewModelClass: VideoModel.self)
|
||||
|
||||
// MARK:- Horizontal Combination Molecules
|
||||
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: ModuleMolecule.self, viewModelClass: ModuleMoleculeModel.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: MoleculeSectionFooter.self, viewModelClass: MoleculeSectionFooterModel.self)
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class HeaderView: Container {
|
||||
public let line = Line()
|
||||
@ -15,7 +14,7 @@ open class HeaderView: Container {
|
||||
open var molecule: MoleculeViewProtocol?
|
||||
|
||||
var headerModel: HeaderModel? {
|
||||
get { return model as? HeaderModel }
|
||||
get { model as? HeaderModel }
|
||||
}
|
||||
|
||||
/// Convenience function to add a molecule to the view.
|
||||
@ -58,6 +57,6 @@ open class HeaderView: Container {
|
||||
}
|
||||
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing
|
||||
PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2019 Suresh, Kamlesh. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class MoleculeHeaderModel: HeaderModel, MoleculeModelProtocol, MoleculeContainerModelProtocol {
|
||||
public static var identifier: String = "header"
|
||||
|
||||
@ -17,7 +17,7 @@ public class MoleculeHeaderView: MoleculeContainer {
|
||||
var line = Line()
|
||||
|
||||
var headerModel: MoleculeHeaderModel? {
|
||||
get { return model as? MoleculeHeaderModel }
|
||||
get { model as? MoleculeHeaderModel }
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {
|
||||
|
||||
@ -15,9 +14,7 @@ import Foundation
|
||||
public let line = Line()
|
||||
|
||||
required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
guard let model = model as? TabBarModel else {
|
||||
fatalError("model is not TabBarModel")
|
||||
}
|
||||
guard let model = model as? TabBarModel else { fatalError("model is not TabBarModel") }
|
||||
self.model = model
|
||||
super.init(frame: .zero)
|
||||
|
||||
@ -32,7 +29,7 @@ import Foundation
|
||||
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 }
|
||||
self.model = model
|
||||
|
||||
@ -57,8 +54,12 @@ import Foundation
|
||||
var tabs: [UITabBarItem] = []
|
||||
for (index, tab) in model.tabs.enumerated() {
|
||||
let tabBarItem = UITabBarItem(title: tab.title, image: MVMCoreCache.shared()?.getImageFromRegisteredBundles(tab.image), tag: index)
|
||||
tabBarItem.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -3)
|
||||
tabBarItem.setTitleTextAttributes([NSAttributedString.Key.font: MFFonts.mfFontTXRegular(8)], for: .normal)
|
||||
tabBarItem.accessibilityLabel = tab.accessibilityText
|
||||
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)
|
||||
}
|
||||
setItems(tabs, animated: false)
|
||||
@ -100,10 +101,7 @@ import Foundation
|
||||
})
|
||||
}
|
||||
|
||||
public func currentTabIndex() -> Int {
|
||||
return model.selectedTab
|
||||
}
|
||||
public func currentTabIndex() -> Int { model.selectedTab }
|
||||
}
|
||||
|
||||
extension UITabBarItem: MFButtonProtocol {
|
||||
}
|
||||
extension UITabBarItem: MFButtonProtocol { }
|
||||
|
||||
@ -60,17 +60,19 @@ public class TabBarModel: MoleculeModelProtocol {
|
||||
}
|
||||
|
||||
public class TabBarItemModel: Codable {
|
||||
var title: String
|
||||
var title: String?
|
||||
var image: String
|
||||
var action: ActionModelProtocol
|
||||
var accessibilityText: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case title
|
||||
case image
|
||||
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.image = image
|
||||
self.action = action
|
||||
@ -78,15 +80,17 @@ public class TabBarItemModel: Codable {
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
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)
|
||||
action = try typeContainer.decodeModel(codingKey: .action)
|
||||
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
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.encodeModel(action, forKey: .action)
|
||||
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,17 +6,15 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers open class ImageBarButtonItem: BarButtonItem {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
public static func create(with image: UIImage?) -> Self {
|
||||
let actionObject = ActionDelegate()
|
||||
let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock(_:)))
|
||||
let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock))
|
||||
button.actionDelegate = actionObject
|
||||
return button
|
||||
}
|
||||
@ -29,7 +27,7 @@ import Foundation
|
||||
}
|
||||
|
||||
/// 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)
|
||||
button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData)
|
||||
return button
|
||||
@ -42,4 +40,3 @@ import Foundation
|
||||
return button
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,10 +6,8 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers open class LabelBarButtonItem: BarButtonItem {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -5,30 +5,47 @@
|
||||
// Created by Scott Pfeil on 5/18/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class NavigationImageButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public var backgroundColor: Color?
|
||||
public var accessibilityIdentifier: String?
|
||||
public static var identifier: String = "navigationImageButton"
|
||||
|
||||
public var image: String
|
||||
public var action: ActionModelProtocol
|
||||
public var accessibilityText: String?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init(with image: String, action: ActionModelProtocol) {
|
||||
self.image = image
|
||||
self.action = action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Coding Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case image
|
||||
case action
|
||||
case accessibilityIdentifier
|
||||
case moleculeName
|
||||
case accessibilityText
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
image = try typeContainer.decode(String.self, forKey: .image)
|
||||
action = try typeContainer.decodeModel(codingKey: .action)
|
||||
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
|
||||
@ -37,19 +54,26 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
|
||||
open func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(image, forKey: .image)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Method
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Convenience function that creates a BarButtonItem for the model.
|
||||
public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem {
|
||||
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 {
|
||||
navigationImageButton.accessibilityLabel = accessibilityString
|
||||
navigationImageButton.isAccessibilityElement = true
|
||||
buttonItem.accessibilityLabel = accessibilityString
|
||||
buttonItem.isAccessibilityElement = true
|
||||
}
|
||||
return navigationImageButton
|
||||
|
||||
return buttonItem
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,28 +6,45 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public var backgroundColor: Color?
|
||||
public static var identifier: String = "navigationLabelButton"
|
||||
|
||||
public var accessibilityIdentifier: String?
|
||||
public var title: String
|
||||
public var action: ActionModelProtocol
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init(with title: String, action: ActionModelProtocol) {
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case accessibilityIdentifier
|
||||
case title
|
||||
case action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
title = try typeContainer.decode(String.self, forKey: .title)
|
||||
action = try typeContainer.decodeModel(codingKey: .action)
|
||||
}
|
||||
@ -35,10 +52,15 @@ public class NavigationLabelButtonModel: NavigationButtonModelProtocol, Molecule
|
||||
open func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Convenience function that creates a BarButtonItem for the model.
|
||||
public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem {
|
||||
return LabelBarButtonItem.create(with: title, actionModel: action, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
@ -6,12 +6,13 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
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 hidden: Bool
|
||||
@ -28,13 +29,21 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc
|
||||
open var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]?
|
||||
open var titleView: MoleculeModelProtocol?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init() {
|
||||
hidden = false
|
||||
backgroundColor = Color(uiColor: .white)
|
||||
tintColor = Color(uiColor: .black)
|
||||
backgroundColor = Color(uiColor: .mvmWhite)
|
||||
tintColor = Color(uiColor: .mvmBlack)
|
||||
line = LineModel(type: .standard)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case title
|
||||
@ -48,13 +57,17 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc
|
||||
case additionalRightButtons
|
||||
case titleView
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
|
||||
hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white)
|
||||
tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .mvmWhite)
|
||||
tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .mvmBlack)
|
||||
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard)
|
||||
alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton)
|
||||
backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton)
|
||||
|
||||
@ -27,6 +27,12 @@ open class BGImageMoleculeModel: MoleculeContainerModel {
|
||||
if bottomPadding == nil {
|
||||
bottomPadding = PaddingDefaultVerticalSpacing3
|
||||
}
|
||||
if image.contentMode == nil {
|
||||
image.contentMode = .scaleAspectFill
|
||||
}
|
||||
if image.imageFormat == nil {
|
||||
image.imageFormat = "jpg"
|
||||
}
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -6,11 +6,10 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
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()
|
||||
guard let model = model as? CollapsableNotificationModel else { return }
|
||||
backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen
|
||||
@ -30,6 +29,10 @@ extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol {
|
||||
MVMCoreUITopAlertBaseView.addAction(to: button, actionMap: topActionMap, additionalData: nil)
|
||||
shortView?.label?.accessibilityTraits = .button
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,17 +6,24 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
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()
|
||||
guard let model = model as? NotificationModel else { return }
|
||||
|
||||
backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen
|
||||
var actionMap = model.button?.action.toJSON()
|
||||
|
||||
if let title = model.button?.title {
|
||||
actionMap?.updateValue(title, forKey: KeyTitle)
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
|
||||
setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate)
|
||||
setup(withMessage: model.headline.text, subMessage: model.body?.text, color: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil)
|
||||
}
|
||||
|
||||
@ -6,52 +6,72 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
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 headline: LabelModel
|
||||
public var body: LabelModel?
|
||||
public var button: ButtonModel?
|
||||
public var closeButton: NotificationXButtonModel?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init(with headline: LabelModel) {
|
||||
self.headline = headline
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Default
|
||||
//--------------------------------------------------
|
||||
|
||||
open func setDefault() {
|
||||
if backgroundColor == nil {
|
||||
backgroundColor = Color(uiColor: .mvmGreen())
|
||||
backgroundColor = Color(uiColor: .mvmGreen)
|
||||
}
|
||||
if headline.textColor == nil {
|
||||
headline.textColor = Color(uiColor: .white)
|
||||
headline.textColor = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
if body?.textColor == nil {
|
||||
body?.textColor = Color(uiColor: .white)
|
||||
body?.textColor = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
if button?.style == nil {
|
||||
button?.style = .secondary
|
||||
}
|
||||
button?.size = .tiny
|
||||
button?.enabledTextColor = Color(uiColor: .white)
|
||||
button?.enabledBorderColor = Color(uiColor: .white)
|
||||
button?.enabledTextColor = Color(uiColor: .mvmWhite)
|
||||
button?.enabledBorderColor = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Coding Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case accessibilityIdentifier
|
||||
case headline
|
||||
case body
|
||||
case button
|
||||
case closeButton
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
headline = try typeContainer.decode(LabelModel.self, forKey: .headline)
|
||||
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
|
||||
button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button)
|
||||
@ -63,6 +83,7 @@ open class NotificationModel: MoleculeModelProtocol {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||
try container.encode(headline, forKey: .headline)
|
||||
try container.encodeIfPresent(body, forKey: .body)
|
||||
try container.encodeIfPresent(button, forKey: .button)
|
||||
|
||||
@ -19,19 +19,15 @@ open class HeadlineBody: View {
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
|
||||
var spaceBetweenLabelsConstant = PaddingOne
|
||||
var spaceBetweenLabelsConstant = Padding.One
|
||||
var spaceBetweenLabels: NSLayoutConstraint?
|
||||
var leftConstraintTitle: NSLayoutConstraint?
|
||||
var rightConstraintTitle: NSLayoutConstraint?
|
||||
var leftConstraintMessage: NSLayoutConstraint?
|
||||
var rightConstraintMessage: NSLayoutConstraint?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
func hasText() -> Bool {
|
||||
return headlineLabel.hasText || messageLabel.hasText
|
||||
headlineLabel.hasText || messageLabel.hasText
|
||||
}
|
||||
|
||||
// MARK: - Styling
|
||||
@ -54,25 +50,25 @@ open class HeadlineBody: View {
|
||||
}
|
||||
}
|
||||
|
||||
func styleLandingPageHeader() {
|
||||
public func styleLandingPageHeader() {
|
||||
headlineLabel.setFontStyle(.Title2XLarge)
|
||||
messageLabel.setFontStyle(.RegularBodySmall)
|
||||
spaceBetweenLabelsConstant = PaddingTwo
|
||||
spaceBetweenLabelsConstant = Padding.Two
|
||||
}
|
||||
|
||||
func stylePageHeader() {
|
||||
public func stylePageHeader() {
|
||||
headlineLabel.setFontStyle(.BoldTitleLarge)
|
||||
messageLabel.setFontStyle(.RegularBodySmall)
|
||||
spaceBetweenLabelsConstant = PaddingOne
|
||||
spaceBetweenLabelsConstant = Padding.One
|
||||
}
|
||||
|
||||
func styleListItem() {
|
||||
public func styleListItem() {
|
||||
headlineLabel.setFontStyle(.BoldBodySmall)
|
||||
messageLabel.setFontStyle(.RegularBodySmall)
|
||||
spaceBetweenLabelsConstant = 0
|
||||
}
|
||||
|
||||
func styleListItemDivider() {
|
||||
public func styleListItemDivider() {
|
||||
headlineLabel.setFontStyle(.BoldTitleMedium)
|
||||
messageLabel.setFontStyle(.RegularBodySmall)
|
||||
spaceBetweenLabelsConstant = 0
|
||||
@ -86,48 +82,41 @@ open class HeadlineBody: View {
|
||||
super.setupView()
|
||||
|
||||
backgroundColor = .clear
|
||||
clipsToBounds = true
|
||||
isAccessibilityElement = false
|
||||
shouldGroupAccessibilityChildren = true
|
||||
accessibilityElements = [headlineLabel, messageLabel]
|
||||
|
||||
let view = MVMCoreUICommonViewsUtility.commonView()
|
||||
addSubview(view)
|
||||
NSLayoutConstraint.constraintPinSubview(toSuperview: view)
|
||||
|
||||
view.isAccessibilityElement = false
|
||||
view.shouldGroupAccessibilityChildren = true
|
||||
view.accessibilityElements = [headlineLabel, messageLabel]
|
||||
|
||||
view.addSubview(headlineLabel)
|
||||
view.addSubview(messageLabel)
|
||||
addSubview(headlineLabel)
|
||||
addSubview(messageLabel)
|
||||
|
||||
headlineLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
headlineLabel.setContentHuggingPriority(.required, for: .vertical)
|
||||
messageLabel.setContentCompressionResistancePriority(.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?.isActive = true
|
||||
|
||||
leftConstraintTitle = headlineLabel.leftAnchor.constraint(equalTo: view.leftAnchor)
|
||||
leftConstraintTitle?.isActive = true
|
||||
|
||||
rightConstraintTitle = view.rightAnchor.constraint(equalTo: headlineLabel.rightAnchor)
|
||||
rightConstraintTitle?.isActive = true
|
||||
|
||||
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
|
||||
NSLayoutConstraint.activate([
|
||||
headlineLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor),
|
||||
messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor),
|
||||
bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
open override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
setSpacing()
|
||||
headlineLabel.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
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return 58
|
||||
}
|
||||
public override class func estimatedHeight(with model: MoleculeModelProtocol,
|
||||
_ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 58 }
|
||||
|
||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
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)
|
||||
messageLabel.setOptional(with: headlineBodyModel.body, delegateObject, additionalData)
|
||||
headlineLabel.setOptional(with: model.headline, delegateObject, additionalData)
|
||||
messageLabel.setOptional(with: model.body, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
|
||||
@ -16,7 +16,7 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
|
||||
open var stackItems: [UIView] = []
|
||||
|
||||
open var stackModel: T? {
|
||||
get { return model as? T }
|
||||
get { model as? T }
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -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 { }
|
||||
}
|
||||
}
|
||||
@ -7,22 +7,16 @@ public enum MolecularError: Swift.Error {
|
||||
}
|
||||
|
||||
|
||||
public protocol MoleculeModelProtocol: ModelProtocol {
|
||||
public protocol MoleculeModelProtocol: ModelProtocol, AccessibilityModelProtocol {
|
||||
var moleculeName: String { get }
|
||||
var backgroundColor: Color? { get set }
|
||||
}
|
||||
|
||||
public extension MoleculeModelProtocol {
|
||||
|
||||
var moleculeName: String {
|
||||
get { return Self.identifier }
|
||||
}
|
||||
var moleculeName: String { Self.identifier }
|
||||
|
||||
static var categoryName: String {
|
||||
return "\(MoleculeModelProtocol.self)"
|
||||
}
|
||||
static var categoryName: String { "\(MoleculeModelProtocol.self)" }
|
||||
|
||||
static var categoryCodingKey: String {
|
||||
return "moleculeName"
|
||||
}
|
||||
static var categoryCodingKey: String { "moleculeName" }
|
||||
}
|
||||
|
||||
@ -29,14 +29,10 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate {
|
||||
super.handleNewData()
|
||||
|
||||
closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { [weak self] _ in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
guard let model = self.templateModel as? ModalListPageTemplateModel, 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)
|
||||
guard let self = self else { return }
|
||||
let closeAction = (self.templateModel as? ModalListPageTemplateModel)?.closeAction ??
|
||||
ActionBackModel()
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -18,15 +18,10 @@ open class ModalMoleculeStackTemplate: MoleculeStackTemplate {
|
||||
override open func handleNewData() {
|
||||
super.handleNewData()
|
||||
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
guard let self = self else { return }
|
||||
let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ??
|
||||
ActionBackModel()
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,12 +19,9 @@ open class ModalSectionListTemplate: SectionListTemplate {
|
||||
super.handleNewData()
|
||||
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in
|
||||
guard let self = self else { return }
|
||||
guard let model = self.templateModel as? ModalSectionListTemplateModel,
|
||||
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)
|
||||
let closeAction = (self.templateModel as? ModalSectionListTemplateModel)?.closeAction ??
|
||||
ActionBackModel()
|
||||
MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,14 @@ import Foundation
|
||||
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() {
|
||||
super.handleNewData()
|
||||
heightConstraint?.isActive = true
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
public typealias BarButtonAction = (BarButtonItem) -> ()
|
||||
|
||||
|
||||
@objc class ActionDelegate: NSObject {
|
||||
var buttonAction: BarButtonAction?
|
||||
@objc func callActionBlock(_ sender: BarButtonItem) {
|
||||
@ -16,7 +17,6 @@ public typealias BarButtonAction = (BarButtonItem) -> ()
|
||||
}
|
||||
|
||||
@objcMembers open class BarButtonItem: UIBarButtonItem, MFButtonProtocol {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Delegate
|
||||
//--------------------------------------------------
|
||||
@ -43,4 +43,3 @@ public typealias BarButtonAction = (BarButtonItem) -> ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
public typealias ButtonAction = (Button) -> ()
|
||||
|
||||
|
||||
@objcMembers open class Button: UIButton, MFButtonProtocol, MoleculeViewProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
@ -65,7 +66,7 @@ public typealias ButtonAction = (Button) -> ()
|
||||
/// Adds a block to be performed for the given event.
|
||||
open func addActionBlock(event: Event, _ buttonBlock: @escaping ButtonAction) {
|
||||
self.buttonAction = buttonBlock
|
||||
addTarget(self, action: #selector(callActionBlock(_:)), for: event)
|
||||
addTarget(self, action: #selector(callActionBlock), for: event)
|
||||
}
|
||||
|
||||
@objc func callActionBlock(_ sender: Button) {
|
||||
@ -103,6 +104,10 @@ public typealias ButtonAction = (Button) -> ()
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
|
||||
if let model = model as? EnableableModelProtocol {
|
||||
isEnabled = model.enabled
|
||||
}
|
||||
@ -119,25 +124,23 @@ public typealias ButtonAction = (Button) -> ()
|
||||
// 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.
|
||||
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? {
|
||||
return nil
|
||||
}
|
||||
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
|
||||
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Accessibility
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func accessibilityActivate() -> Bool {
|
||||
guard isEnabled else { return false }
|
||||
buttonAction?(self)
|
||||
return buttonAction != nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
@ -160,6 +163,6 @@ extension Button: MVMCoreViewProtocol {
|
||||
extension Button: AppleGuidelinesProtocol {
|
||||
|
||||
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
return Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
||||
Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,9 +51,14 @@ import UIKit
|
||||
// MARK:- MoleculeViewProtocol
|
||||
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
self.model = model
|
||||
|
||||
if let backgroundColor = model.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
@ -63,22 +68,18 @@ import UIKit
|
||||
// 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.
|
||||
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? {
|
||||
return nil
|
||||
}
|
||||
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
|
||||
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
|
||||
}
|
||||
|
||||
// MARK: - AppleGuidelinesProtocol
|
||||
extension Control: AppleGuidelinesProtocol {
|
||||
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
return Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
||||
Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,33 +53,35 @@ open class ImageView: UIImageView, MoleculeViewProtocol {
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
backgroundColor = .clear
|
||||
}
|
||||
|
||||
backgroundColor = .clear
|
||||
}
|
||||
|
||||
public func setAsMolecule() { }
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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
|
||||
|
||||
if let backgroundColor = model.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
return model?.moleculeName
|
||||
model?.moleculeName
|
||||
}
|
||||
|
||||
open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return nil
|
||||
}
|
||||
open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
|
||||
|
||||
open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
|
||||
}
|
||||
|
||||
// MARK:- MVMCoreViewProtocol
|
||||
@ -94,4 +96,3 @@ extension ImageView: MVMCoreViewProtocol {
|
||||
MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers open class SectionHeaderFooterView: UITableViewHeaderFooterView, MoleculeViewProtocol {
|
||||
//--------------------------------------------------
|
||||
@ -43,10 +42,16 @@ import Foundation
|
||||
//--------------------------------------------------
|
||||
|
||||
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
|
||||
self.model = model
|
||||
|
||||
if let backgroundColor = model.backgroundColor {
|
||||
contentView.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
@ -56,16 +61,12 @@ import Foundation
|
||||
// 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.
|
||||
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? {
|
||||
return nil
|
||||
}
|
||||
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
|
||||
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
|
||||
}
|
||||
|
||||
// MARK:- MVMCoreViewProtocol
|
||||
|
||||
@ -11,7 +11,7 @@ import Foundation
|
||||
@objcMembers open class TableView: UITableView {
|
||||
|
||||
/// A block that gets called on tableview frame changes
|
||||
public var frameChangeAction: (() -> Void)?
|
||||
public var frameChangeAction: (() -> ())?
|
||||
|
||||
private var previousFrame = CGRect.zero
|
||||
|
||||
|
||||
@ -93,6 +93,10 @@ extension TextField: MoleculeViewProtocol {
|
||||
if let color = model.backgroundColor?.uiColor {
|
||||
backgroundColor = color
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
|
||||
@ -106,8 +106,8 @@ import UIKit
|
||||
|
||||
open func reset() {
|
||||
|
||||
fontStyle = Styler.Font.RegularBodyLarge
|
||||
placeholderFontStyle = Styler.Font.RegularMicro
|
||||
fontStyle = .RegularBodyLarge
|
||||
placeholderFontStyle = .RegularMicro
|
||||
placeholderTextColor = .mvmCoolGray3
|
||||
textColor = .mvmBlack
|
||||
isEnabled = true
|
||||
|
||||
@ -58,6 +58,10 @@ import UIKit
|
||||
if let backgroundColor = model.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
|
||||
if let accessibilityIdentifier = model.accessibilityIdentifier {
|
||||
self.accessibilityIdentifier = accessibilityIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
@ -67,16 +71,12 @@ import UIKit
|
||||
// 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.
|
||||
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? {
|
||||
return nil
|
||||
}
|
||||
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil }
|
||||
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { nil }
|
||||
}
|
||||
|
||||
// MARK:- MVMCoreViewProtocol
|
||||
|
||||
@ -73,6 +73,12 @@ open class ScrollingViewController: ViewController {
|
||||
scrollView.flashScrollIndicators()
|
||||
}
|
||||
|
||||
open func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
executeBehaviors { (behavior: PageScrolledBehavior) in
|
||||
behavior.pageScrolled(scrollView: scrollView)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keyboard Handling
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -74,7 +74,7 @@ import UIKit
|
||||
}
|
||||
|
||||
open func modulesToListenFor() -> [String]? {
|
||||
loadObject?.requestParameters?.modules as? [String]
|
||||
loadObject?.requestParameters?.allModules()
|
||||
}
|
||||
|
||||
@objc open func responseJSONUpdated(notification: Notification) {
|
||||
@ -348,8 +348,8 @@ import UIKit
|
||||
|
||||
// First update should be explicit (hence the zero check)
|
||||
if needsUpdateUI || (previousScreenSize != .zero && screenSizeChanged()) {
|
||||
updateViews()
|
||||
needsUpdateUI = false
|
||||
updateViews()
|
||||
}
|
||||
|
||||
previousScreenSize = view.bounds.size;
|
||||
|
||||
@ -10,23 +10,22 @@ import Foundation
|
||||
|
||||
public protocol PageBehaviorProtocol: ModelProtocol {
|
||||
|
||||
// The type of rule
|
||||
/// The type of rule
|
||||
var behaviorName: String { get }
|
||||
|
||||
}
|
||||
|
||||
public extension PageBehaviorProtocol {
|
||||
|
||||
var behaviorName: String {
|
||||
get { return Self.identifier }
|
||||
get { Self.identifier }
|
||||
}
|
||||
|
||||
static var categoryCodingKey: String {
|
||||
return "behaviorName"
|
||||
"behaviorName"
|
||||
}
|
||||
|
||||
static var categoryName: String {
|
||||
return "\(PageBehaviorProtocol.self)"
|
||||
"\(PageBehaviorProtocol.self)"
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,11 +33,31 @@ public protocol PageVisibilityBehavior: PageBehaviorProtocol {
|
||||
|
||||
func onPageShown()
|
||||
func onPageHidden()
|
||||
}
|
||||
|
||||
public protocol PageScrolledBehavior: PageBehaviorProtocol {
|
||||
|
||||
func pageScrolled(scrollView: UIScrollView)
|
||||
}
|
||||
|
||||
public protocol PageBehaviorsTemplateProtocol {
|
||||
|
||||
var behaviors: [PageBehaviorProtocol]? { get }
|
||||
|
||||
var behaviors: [PageBehaviorProtocol]? { get set }
|
||||
|
||||
}
|
||||
|
||||
public extension PageBehaviorsTemplateProtocol {
|
||||
mutating func add(behavior: PageBehaviorProtocol) {
|
||||
var newBehaviors = behaviors ?? []
|
||||
newBehaviors.append(behavior)
|
||||
self.behaviors = newBehaviors
|
||||
}
|
||||
}
|
||||
|
||||
public extension MVMCoreUIDelegateObject {
|
||||
weak var behaviorTemplateDelegate: (PageBehaviorsTemplateProtocol & NSObjectProtocol)? {
|
||||
get {
|
||||
return (moleculeDelegate as? PageProtocol)?.pageModel as? (PageBehaviorsTemplateProtocol & NSObjectProtocol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
MVMCoreUI/Behaviors/PageScrolledClosureBehavior.swift
Normal file
33
MVMCoreUI/Behaviors/PageScrolledClosureBehavior.swift
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// PageScrolledClosureBehavior.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 2/11/21.
|
||||
// Copyright © 2021 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class PageScrolledClosureBehavior: PageScrolledBehavior {
|
||||
|
||||
public static var identifier = "pageScrolledClosureBehavior"
|
||||
|
||||
public var pageScrolledHandler: (_ scrollView: UIScrollView) -> Void
|
||||
|
||||
public init(with onPageScrolledHandler: @escaping (_ scrollView: UIScrollView) -> Void) {
|
||||
self.pageScrolledHandler = onPageScrolledHandler
|
||||
}
|
||||
|
||||
// This class is not meant to be decoded and encoded really.
|
||||
public required init(from decoder: Decoder) throws {
|
||||
throw ModelRegistry.Error.decoderOther(message: "PageScrolledClosureBehavior does not decode.")
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
throw ModelRegistry.Error.decoderOther(message: "PageScrolledClosureBehavior does not encode.")
|
||||
}
|
||||
|
||||
public func pageScrolled(scrollView: UIScrollView) {
|
||||
pageScrolledHandler(scrollView)
|
||||
}
|
||||
}
|
||||
40
MVMCoreUI/Behaviors/PageVisibilityClosureBehavior.swift
Normal file
40
MVMCoreUI/Behaviors/PageVisibilityClosureBehavior.swift
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// PageVisibilityClosureBehavior.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 2/11/21.
|
||||
// Copyright © 2021 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class PageVisibilityClosureBehavior: PageVisibilityBehavior {
|
||||
|
||||
public static var identifier = "pageVisibilityClosureBehavior"
|
||||
|
||||
public var pageShownHandler: () -> Void
|
||||
public var pageHiddenHandler: () -> Void
|
||||
|
||||
public init(with onPageShownHandler: @escaping () -> Void, onPageHiddenHandler: @escaping () -> Void) {
|
||||
self.pageShownHandler = onPageShownHandler
|
||||
self.pageHiddenHandler = onPageHiddenHandler
|
||||
}
|
||||
|
||||
// This class is not meant to be decoded and encoded really.
|
||||
public required init(from decoder: Decoder) throws {
|
||||
throw ModelRegistry.Error.decoderOther(message: "PageVisibilityClosureBehavior does not decode.")
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
throw ModelRegistry.Error.decoderOther(message: "PageVisibilityClosureBehavior does not encode.")
|
||||
}
|
||||
|
||||
//MARK:- PageVisibilityBehavior
|
||||
public func onPageShown() {
|
||||
pageShownHandler()
|
||||
}
|
||||
|
||||
public func onPageHidden() {
|
||||
pageHiddenHandler()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user