merge develop

This commit is contained in:
Pfeil, Scott Robert 2019-11-05 09:14:19 -05:00
commit 5b8fce36e6
64 changed files with 2053 additions and 602 deletions

View File

@ -3,15 +3,21 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 48;
objects = {
/* Begin PBXBuildFile section */
01004F3022721C3800991ECC /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01004F2F22721C3800991ECC /* RadioButton.swift */; };
0105618D224BBE7700E1557D /* FormValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618A224BBE7700E1557D /* FormValidator.swift */; };
0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */; };
0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */; };
016A1071228122180009D605 /* HeadlineBodyTextButtonSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016A1070228122180009D605 /* HeadlineBodyTextButtonSwitch.swift */; };
017BEB3A2360EEB40024EF95 /* PageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB392360EEB40024EF95 /* PageModel.swift */; };
0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0116A4E4228B19640094F3ED /* RadioButtonModel.swift */; };
01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */; };
01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D902327ECE600EF99AA /* CornerLabels.swift */; };
01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D922327ECFB00EF99AA /* ProgressBar.swift */; };
01509D952327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D942327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift */; };
017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */; };
017BEB3A2360EEB40024EF95 /* PageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB392360EEB40024EF95 /* PageModel.swift */; };
017BEB3C2361EA1D0024EF95 /* MFViewController+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */; };
017BEB4023620A230024EF95 /* TextFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB3F23620A230024EF95 /* TextFieldModel.swift */; };
017BEB4223620AD20024EF95 /* FormModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB4123620AD20024EF95 /* FormModelProtocol.swift */; };
@ -42,9 +48,12 @@
01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368D23609801006832FA /* HeadlineBodyModel.swift */; };
01EB369523609801006832FA /* ActionMapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368E23609801006832FA /* ActionMapModel.swift */; };
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; };
0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; };
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; };
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; };
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; };
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; };
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; };
B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; };
B8200E192281DC1A007245F4 /* CornerLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E182281DC1A007245F4 /* CornerLabels.swift */; };
D206997721FB8A0B00CAE0DE /* MVMCoreUINavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = D206997521FB8A0B00CAE0DE /* MVMCoreUINavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
D206997821FB8A0B00CAE0DE /* MVMCoreUINavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = D206997621FB8A0B00CAE0DE /* MVMCoreUINavigationController.m */; };
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; };
@ -65,6 +74,7 @@
D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */; };
D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* StandardFooterView.swift */; };
D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2755D7A23689C7500485468 /* TableViewCell.swift */; };
D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */; };
D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */; };
D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */; };
@ -174,7 +184,6 @@
D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF14721E68728003B2FB9 /* MFSizeThreshold.m */; };
D29DF2CE21E7C104003B2FB9 /* MFLoadingViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */; };
D29DF2D121E7C1C8003B2FB9 /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */; };
D29DF2E121E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF2E021E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF2EE21ECEADF003B2FB9 /* MFFonts.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF14D21E693AD003B2FB9 /* MFFonts.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF14C21E693AD003B2FB9 /* MFFonts.m */; };
@ -199,13 +208,14 @@
D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A638FC22CA98280052ED1F /* HeadlineBody.swift */; };
D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390022CBB1820052ED1F /* Carousel.swift */; };
D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */; };
D2B18B7F2360913400A9AEDC /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B7E2360913400A9AEDC /* Control.swift */; };
D2B18B812360945C00A9AEDC /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B802360945C00A9AEDC /* View.swift */; };
D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */; };
D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; };
D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; };
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; };
D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */; };
D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */; };
D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; };
D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; };
DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; };
@ -217,11 +227,17 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
01004F2F22721C3800991ECC /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = "<group>"; };
0105618A224BBE7700E1557D /* FormValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormValidator.swift; sourceTree = "<group>"; };
0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+TextFields.swift"; sourceTree = "<group>"; };
0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+FormParams.swift"; sourceTree = "<group>"; };
016A1070228122180009D605 /* HeadlineBodyTextButtonSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyTextButtonSwitch.swift; sourceTree = "<group>"; };
017BEB392360EEB40024EF95 /* PageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageModel.swift; sourceTree = "<group>"; };
0116A4E4228B19640094F3ED /* RadioButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = "<group>"; };
01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = "<group>"; };
01509D902327ECE600EF99AA /* CornerLabels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CornerLabels.swift; sourceTree = "<group>"; };
01509D922327ECFB00EF99AA /* ProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
01509D942327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyTextButtonSwitch.swift; sourceTree = "<group>"; };
017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonLabel.swift; sourceTree = "<group>"; };
017BEB392360EEB40024EF95 /* PageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageModel.swift; sourceTree = "<group>"; };
017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFViewController+Model.swift"; sourceTree = "<group>"; };
017BEB3F23620A230024EF95 /* TextFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldModel.swift; sourceTree = "<group>"; };
017BEB4123620AD20024EF95 /* FormModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormModelProtocol.swift; sourceTree = "<group>"; };
@ -251,9 +267,12 @@
01EB368D23609801006832FA /* HeadlineBodyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyModel.swift; sourceTree = "<group>"; };
01EB368E23609801006832FA /* ActionMapModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionMapModel.swift; sourceTree = "<group>"; };
0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = "<group>"; };
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = "<group>"; };
0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = "<group>"; };
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = "<group>"; };
9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = "<group>"; };
948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = "<group>"; };
B8200E142280C4CF007245F4 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
B8200E182281DC1A007245F4 /* CornerLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabels.swift; sourceTree = "<group>"; };
D206997521FB8A0B00CAE0DE /* MVMCoreUINavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUINavigationController.h; sourceTree = "<group>"; };
D206997621FB8A0B00CAE0DE /* MVMCoreUINavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUINavigationController.m; sourceTree = "<group>"; };
D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = "<group>"; };
@ -274,6 +293,7 @@
D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIPageControl.m; sourceTree = "<group>"; };
D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = "<group>"; };
D274CA322236A78900B01B62 /* StandardFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardFooterView.swift; sourceTree = "<group>"; };
D2755D7A23689C7500485468 /* TableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = "<group>"; };
D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTableViewCell.swift; sourceTree = "<group>"; };
D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyebrowHeadlineBodyLink.swift; sourceTree = "<group>"; };
D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFLoadImageView.swift; sourceTree = "<group>"; };
@ -387,7 +407,6 @@
D29DF2C321E7BF57003B2FB9 /* MFTabBarInteractor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFTabBarInteractor.m; sourceTree = "<group>"; };
D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFLoadingViewController.h; sourceTree = "<group>"; };
D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFLoadingViewController.m; sourceTree = "<group>"; };
D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = "<group>"; };
D29DF2E021E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPanelProtocol.h; sourceTree = "<group>"; };
D29DF31621ECECC0003B2FB9 /* NHaasGroteskDSStd-45Lt.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NHaasGroteskDSStd-45Lt.otf"; sourceTree = "<group>"; };
D29DF31721ECECC0003B2FB9 /* OCRAExtended.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = OCRAExtended.ttf; sourceTree = "<group>"; };
@ -412,13 +431,14 @@
D2A638FC22CA98280052ED1F /* HeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBody.swift; sourceTree = "<group>"; };
D2A6390022CBB1820052ED1F /* Carousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Carousel.swift; sourceTree = "<group>"; };
D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeCollectionViewCell.swift; sourceTree = "<group>"; };
D2B18B7E2360913400A9AEDC /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = "<group>"; };
D2B18B802360945C00A9AEDC /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeadlineBody.swift; sourceTree = "<group>"; };
D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = "<group>"; };
D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = "<group>"; };
D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = "<group>"; };
D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = "<group>"; };
D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIDelegateObject.swift; sourceTree = "<group>"; };
D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = "<group>"; };
D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = "<group>"; };
D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = "<group>"; };
DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = "<group>"; };
@ -435,7 +455,7 @@
buildActionMask = 2147483647;
files = (
D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */,
D29DF2D121E7C1C8003B2FB9 /* MVMAnimationFramework.framework in Frameworks */,
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -498,9 +518,9 @@
D224798823142BF2003FCCF9 /* SwitchMolecules */ = {
isa = PBXGroup;
children = (
01509D942327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift */,
D22479892314445E003FCCF9 /* LabelSwitch.swift */,
D224798B231450C8003FCCF9 /* HeadlineBodySwitch.swift */,
016A1070228122180009D605 /* HeadlineBodyTextButtonSwitch.swift */,
);
path = SwitchMolecules;
sourceTree = "<group>";
@ -527,8 +547,8 @@
D224798F2316A99F003FCCF9 /* LeftRightViews */ = {
isa = PBXGroup;
children = (
01509D902327ECE600EF99AA /* CornerLabels.swift */,
D224798823142BF2003FCCF9 /* SwitchMolecules */,
B8200E182281DC1A007245F4 /* CornerLabels.swift */,
);
path = LeftRightViews;
sourceTree = "<group>";
@ -545,8 +565,9 @@
D22479912316A9EF003FCCF9 /* Items */ = {
isa = PBXGroup;
children = (
D2755D7A23689C7500485468 /* TableViewCell.swift */,
01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */,
D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */,
D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */,
D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */,
D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */,
);
@ -587,6 +608,7 @@
isa = PBXGroup;
children = (
01509D96232803B200EF99AA /* Models */,
D2B18B7D236090D500A9AEDC /* BaseClasses */,
01C74D87224298E2009C25A3 /* FormUIHelpers */,
D29DF31421ECECA7003B2FB9 /* SupportingFiles */,
D29DF27021E79B2C003B2FB9 /* OtherHandlers */,
@ -623,8 +645,8 @@
D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = {
isa = PBXGroup;
children = (
D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */,
D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */,
9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -653,8 +675,11 @@
D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */,
D2A514662213885800345BFB /* StandardHeaderView.swift */,
D274CA322236A78900B01B62 /* StandardFooterView.swift */,
0116A4E4228B19640094F3ED /* RadioButtonModel.swift */,
D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */,
D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */,
0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */,
017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */,
017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */,
017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */,
);
@ -744,6 +769,7 @@
D29DF2A021E7AF4E003B2FB9 /* MVMCoreUIUtility.m */,
D29DF2A721E7B2F9003B2FB9 /* MVMCoreUIConstants.h */,
D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */,
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */,
);
path = Utility;
sourceTree = "<group>";
@ -779,10 +805,10 @@
D29DF17D21E69E26003B2FB9 /* Views */ = {
isa = PBXGroup;
children = (
01509D922327ECFB00EF99AA /* ProgressBar.swift */,
D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */,
D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */,
D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */,
B8200E142280C4CF007245F4 /* ProgressBar.swift */,
948DB67D2326DCD90011F916 /* MultiProgress.swift */,
DBC4391622442196001AB423 /* CaretView.swift */,
DBC4391722442197001AB423 /* DashLine.swift */,
@ -816,6 +842,9 @@
DB891E822253FA8500022516 /* Label.swift */,
0198F7A02256A80A0066C936 /* MFRadioButton.h */,
0198F7A22256A80A0066C936 /* MFRadioButton.m */,
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */,
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */,
01004F2F22721C3800991ECC /* RadioButton.swift */,
);
path = Views;
sourceTree = "<group>";
@ -936,6 +965,15 @@
path = Strings;
sourceTree = "<group>";
};
D2B18B7D236090D500A9AEDC /* BaseClasses */ = {
isa = PBXGroup;
children = (
D2B18B7E2360913400A9AEDC /* Control.swift */,
D2B18B802360945C00A9AEDC /* View.swift */,
);
path = BaseClasses;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -1049,11 +1087,12 @@
D29DF0CB21E404D4003B2FB9 = {
CreatedOnToolsVersion = 10.1;
LastSwiftMigration = 1010;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = D29DF0C621E404D4003B2FB9 /* Build configuration list for PBXProject "MVMCoreUI" */;
compatibilityVersion = "Xcode 9.3";
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@ -1100,17 +1139,21 @@
D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */,
D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */,
DBC4391922442197001AB423 /* DashLine.swift in Sources */,
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */,
D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */,
0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */,
017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */,
D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */,
D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */,
D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */,
01EB3686236097F1006832FA /* MoleculeHolder.swift in Sources */,
D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */,
01004F3022721C3800991ECC /* RadioButton.swift in Sources */,
017BEB3C2361EA1D0024EF95 /* MFViewController+Model.swift in Sources */,
D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */,
D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */,
D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */,
D2B18B7F2360913400A9AEDC /* Control.swift in Sources */,
01EB3681236092AE006832FA /* Holder.swift in Sources */,
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */,
DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */,
@ -1133,17 +1176,20 @@
017BEB4023620A230024EF95 /* TextFieldModel.swift in Sources */,
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */,
D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */,
01EB369223609801006832FA /* MoleculeStackModel.swift in Sources */,
D29DF25421E6A177003B2FB9 /* MFMdnTextField.m in Sources */,
B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */,
D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */,
D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */,
01EB369023609801006832FA /* ListItemModel.swift in Sources */,
DBEFFA04225A829700230692 /* Label.swift in Sources */,
D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */,
01509D952327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift in Sources */,
D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */,
D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */,
0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */,
D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */,
D2B18B812360945C00A9AEDC /* View.swift in Sources */,
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */,
01EB367923609281006832FA /* JSONValue.swift in Sources */,
D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */,
@ -1152,9 +1198,10 @@
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */,
01EB368F23609801006832FA /* LabelModel.swift in Sources */,
0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */,
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */,
D22479962316AF6E003FCCF9 /* HeadlineBodyTextButton.swift in Sources */,
D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */,
D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */,
017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */,
D29DF18121E69E50003B2FB9 /* MFView.m in Sources */,
D29DF18321E69E54003B2FB9 /* SeparatorView.m in Sources */,
D29DF17A21E69E1F003B2FB9 /* MFCustomButton.m in Sources */,
@ -1171,7 +1218,6 @@
D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */,
D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */,
D29DF2C721E7BF57003B2FB9 /* MFTabBarInteractor.m in Sources */,
016A1071228122180009D605 /* HeadlineBodyTextButtonSwitch.swift in Sources */,
D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */,
D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */,
D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */,
@ -1206,6 +1252,7 @@
D29DF25121E6A177003B2FB9 /* MFDigitTextBox.m in Sources */,
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */,
0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */,
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */,
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */,
0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */,
@ -1215,11 +1262,13 @@
D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */,
D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */,
D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */,
B8200E192281DC1A007245F4 /* CornerLabels.swift in Sources */,
D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */,
01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */,
0105618D224BBE7700E1557D /* FormValidator.swift in Sources */,
01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */,
D22D1F1B220341F60077CEC0 /* MVMCoreUICheckBox.m in Sources */,
D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */,
01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */,
D29770F521F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1293,7 +1342,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@ -1352,12 +1401,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@ -1379,12 +1427,8 @@
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks";
INFOPLIST_FILE = MVMCoreUI/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
@ -1409,12 +1453,8 @@
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks";
INFOPLIST_FILE = MVMCoreUI/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;

View File

@ -64,7 +64,7 @@ import UIKit
primaryButton?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 42
}

View File

@ -140,7 +140,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
return UIStackView.Alignment.leading;
}
public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 10
}
}

View File

@ -13,13 +13,15 @@
#import "MFStyler.h"
#import "UIColor+MFConvenience.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@import MVMCore.MVMCoreDispatchUtility;
@import MVMCore.MVMCoreGetterUtility;
@import MVMCore.NSDictionary_MFConvenience;
@interface PrimaryButton() <FormValidationProtocol>
@interface PrimaryButton() <FormValidationEnableDisableProtocol>
@property (nonatomic) BOOL validationRequired;
@property (nonatomic, strong) NSArray *requiredGroupsList;
@property (nonatomic) BOOL smallButton;
@property (assign, nonatomic) BOOL tinyButton;
@property (nonatomic) CGFloat sizeForSizing;
@ -666,6 +668,9 @@
}
- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData {
self.validationRequired = [json boolForKey:@"validationRequired"];
self.requiredGroupsList = [json array:@"requiredGroups"];
[FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol];
self.primaryButtonType = PrimaryButtonTypeCustom;
@ -696,7 +701,6 @@
if ((color = [json string:@"disabledBorderColor"])) {
self.disabledBorderColor = [UIColor mfGetColorForHex:color];
}
self.validationRequired = [json boolForKey:@"validationRequired"];
NSString *size = [json string:@"size"];
if ([size isEqualToString:@"small"]) {
@ -773,7 +777,15 @@
}
}
#pragma mark - FormValidationProtocol
#pragma mark - FormValidationEnableDisableProtocol
- (BOOL)isValidationRequired {
return self.validationRequired;
}
- (NSArray<NSString *> *)requiredGroups {
return self.requiredGroupsList;
}
- (void)enableField:(BOOL)enable {
if (!self.validationRequired) {

View File

@ -46,7 +46,8 @@
// To set the placeholder and text
@property (nullable, weak, nonatomic) NSString *text;
@property (nullable, weak, nonatomic) NSString *formText;
@property (nullable, weak, nonatomic) NSString *fieldKey;
@property (nullable, strong, nonatomic) NSString *fieldKey;
@property (nullable, strong, nonatomic) NSString *groupName;
@property (nullable, weak, nonatomic) NSString *placeholder; // will move out in Feb release

View File

@ -18,7 +18,7 @@
@import MVMCore.NSDictionary_MFConvenience;
@import MVMCore.MVMCoreJSONConstants;
@interface MFTextField() <FormValidationProtocol>
@interface MFTextField() <FormValidationFormFieldProtocol>
@property (strong, nonatomic) UIColor *customPlaceHolderColor;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *separatorHeightConstraint;
@ -83,11 +83,9 @@
self.enabled = YES;
// Disable SmartQuotes
if (@available(iOS 11.0, *)) {
self.textField.smartQuotesType = UITextSmartQuotesTypeNo;
self.textField.smartDashesType = UITextSmartDashesTypeNo;
self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
}
self.textField.smartQuotesType = UITextSmartQuotesTypeNo;
self.textField.smartDashesType = UITextSmartDashesTypeNo;
self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
}
}
@ -319,12 +317,14 @@
if (string.length > 0) {
self.errMessage = string;
}
// key used to send text value to server
string = [map string:KeyFieldKey];
if (string.length > 0) {
self.fieldKey = string;
}
self.groupName = [map string:@"groupName"];
string = [map string:KeyType];
if ([string isEqualToString:@"dropDown"]) {
@ -588,5 +588,9 @@
- (nullable id)formFieldValue {
return self.text;
}
- (NSString * _Nullable)formFieldGroupName {
return self.groupName;
}
@end

View File

@ -0,0 +1,480 @@
//
// Checkbox.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 9/13/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import MVMCore
/**
This class expects its height and width to be equal.
*/
@objcMembers open class Checkbox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
// Form Validation
var isRequired = false
var fieldKey: String?
var groupName: String?
var delegateObject: MVMCoreUIDelegateObject?
public static let defaultHeightWidth: CGFloat = 18.0
/// If true the border of this checkbox will be circular.
public var isRound: Bool = false
/// Determined if the checkbox's UI should animated when selected.
public var isAnimated: Bool = true
/// Disables all selection logic when setting the value of isSelected, reducing it to a stored property.
public var updateSelectionOnly: Bool = false
/// The color of the background when checked.
public var checkedBackgroundColor: UIColor = .clear {
didSet {
if isSelected {
backgroundColor = checkedBackgroundColor
}
}
}
/// The color of the background when unChecked.
public var unCheckedBackgroundColor: UIColor = .clear {
didSet {
if !isSelected {
backgroundColor = unCheckedBackgroundColor
}
}
}
/// Retrieves ideeal radius value to curve square into a circle.
public var cornerRadiusValue: CGFloat {
return bounds.size.height / 2
}
/// Action Block called when the switch is selected.
public var actionBlock: ActionBlock?
/// Manages the appearance of the checkbox.
private var shapeLayer: CAShapeLayer?
/// Width of the check mark.
public var checkWidth: CGFloat = 2 {
didSet {
if let shapeLayer = shapeLayer {
CATransaction.withDisabledAnimations {
shapeLayer.lineWidth = checkWidth
}
}
}
}
/// Color of the check mark.
public var checkColor: UIColor = .black {
didSet {
setShapeLayerStrokeColor(checkColor)
}
}
/// Border width of the checkbox
public var borderWidth: CGFloat = 1 {
didSet {
layer.borderWidth = borderWidth
}
}
/// border color of the Checkbox
public var borderColor: UIColor = .black {
didSet {
layer.borderColor = borderColor.cgColor
}
}
/**
The represented state of the Checkbox.
Setting updateSelectionOnly to true bypasses the animation logic inherent with setting this property.
*/
override open var isSelected: Bool {
didSet {
if !updateSelectionOnly {
layoutIfNeeded()
shapeLayer?.removeAllAnimations()
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol)
updateAccessibilityLabel()
}
}
}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
private var heightConstraint: NSLayoutConstraint?
private var widthConstraint: NSLayoutConstraint?
/// Updates the height and width anchors of the Checkbox with the assigned value.
public var heigthWidthConstant: CGFloat = Checkbox.defaultHeightWidth {
didSet {
heightConstraint?.constant = heigthWidthConstant
widthConstraint?.constant = heigthWidthConstant
}
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
override public init(frame: CGRect) {
super.init(frame: frame)
accessibilityTraits = .button
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint")
updateAccessibilityLabel()
setupView()
}
/// There is currently no intention on using xib files.
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("xib file is not implemented for Checkbox.")
}
public convenience init() {
self.init(frame:.zero)
}
public convenience init(isChecked: Bool) {
self.init(frame: .zero)
updateSelectionOnly = true
isSelected = isChecked
updateSelectionOnly = false
}
public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) {
self.init(frame: .zero)
updateSelectionOnly = true
isSelected = isChecked
updateSelectionOnly = false
self.checkedBackgroundColor = checkedBackgroundColor
self.unCheckedBackgroundColor = unCheckedBackgroundColor
}
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
override open func layoutSubviews() {
super.layoutSubviews()
drawShapeLayer()
layer.cornerRadius = isRound ? cornerRadiusValue : 0
layer.borderWidth = borderWidth
layer.borderColor = borderColor.cgColor
}
open func setupView() {
guard constraints.isEmpty else { return }
isUserInteractionEnabled = true
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .clear
widthConstraint = widthAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth)
heightConstraint = heightAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth)
heightWidthIsActive(true)
}
//--------------------------------------------------
// MARK: - Actions
//--------------------------------------------------
open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
super.sendAction(action, to: target, for: event)
toggleAndAction()
}
open override func sendActions(for controlEvents: UIControl.Event) {
super.sendActions(for: controlEvents)
toggleAndAction()
}
/// This will toggle the state of the Checkbox and execute the actionBlock if provided.
public func toggleAndAction() {
isSelected.toggle()
actionBlock?()
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Creates the check mark layer.
private func drawShapeLayer() {
if shapeLayer == nil {
let shapeLayer = CAShapeLayer()
self.shapeLayer = shapeLayer
shapeLayer.frame = bounds
layer.addSublayer(shapeLayer)
shapeLayer.strokeColor = checkColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.path = checkMarkPath()
shapeLayer.lineJoin = .miter
shapeLayer.lineWidth = checkWidth
CATransaction.withDisabledAnimations {
shapeLayer.strokeEnd = isSelected ? 1 : 0
}
}
}
/// - returns: The CGPath of a UIBezierPath detailing the path of a checkmark
func checkMarkPath() -> CGPath {
let length = max(bounds.size.height, bounds.size.width)
let xInsetLeft = length * 0.25
let yInsetTop = length * 0.3
let innerWidth = length - (xInsetLeft + length * 0.25) // + Right X Inset
let innerHeight = length - (yInsetTop + length * 0.35) // + Bottom Y Inset
let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2))
let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight)
let endOffset = CGPoint(x: xInsetLeft + innerWidth, y: yInsetTop)
let bezierPath = UIBezierPath()
bezierPath.move(to: startPoint)
bezierPath.addLine(to: pivotOffSet)
bezierPath.addLine(to: endOffset)
return bezierPath.cgPath
}
/// Programmatic means to check/uncheck the box.
/// - parameter selected: state of the check box: true = checked OR false = unchecked.
/// - parameter animated: allows the state of the checkbox to change with or without animation.
public func updateSelection(to selected: Bool, animated: Bool) {
DispatchQueue.main.async {
self.updateSelectionOnly = true
self.isSelected = selected
self.updateSelectionOnly = false
self.drawShapeLayer()
self.shapeLayer?.removeAllAnimations()
self.updateCheckboxUI(isSelected: selected, isAnimated: animated)
}
}
/// updates the visuals of the check mark and background.
/// - parameter isSelected: the check state of the checkbox.
/// - parameter isAnimated: determines of the changes should animate or immediately refelect.
public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) {
if isAnimated {
let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear)
animateStrokeEnd.duration = 0.3
animateStrokeEnd.fillMode = .both
animateStrokeEnd.isRemovedOnCompletion = false
animateStrokeEnd.fromValue = !isSelected ? 1 : 0
animateStrokeEnd.toValue = isSelected ? 1 : 0
self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd")
UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: {
self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
})
} else {
CATransaction.withDisabledAnimations {
self.shapeLayer?.strokeEnd = isSelected ? 1 : 0
}
backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor
}
}
/// Adjust accessibility label based on state of Checkbox.
func updateAccessibilityLabel() {
// Attention: This needs to be addressed with the accessibility team.
// NOTE: Currently emptying description part of MVMCoreUICheckBox accessibility label to avoid crashing!
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") {
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@%@", "", state)
}
}
func isEnabled(_ enabled: Bool) {
isUserInteractionEnabled = enabled
if enabled {
layer.borderColor = borderColor.cgColor
backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor
alpha = 1.0
setShapeLayerStrokeColor(checkColor)
} else {
layer.borderColor = UIColor.mfSilver().cgColor
backgroundColor = .clear
alpha = DisableOppacity
setShapeLayerStrokeColor(UIColor.mfSilver())
}
}
private func setShapeLayerStrokeColor(_ color: UIColor) {
if let shapeLayer = shapeLayer {
CATransaction.withDisabledAnimations {
shapeLayer.strokeColor = color.cgColor
}
}
}
public func heightWidthIsActive(_ isActive: Bool) {
heightConstraint?.isActive = isActive
widthConstraint?.isActive = isActive
}
//--------------------------------------------------
// MARK: - UITouch
//--------------------------------------------------
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
sendActions(for: touchIsAcceptablyOutside(touches.first) ? .touchUpOutside : .touchUpInside)
}
func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool {
let endLocation = touch?.location(in: self)
let x = endLocation?.x ?? 0.0
let y = endLocation?.y ?? 0.0
let faultTolerance: CGFloat = 20.0
let widthLimit = CGFloat(bounds.size.width + faultTolerance)
let heightLimt = CGFloat(bounds.size.height + faultTolerance)
return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt
}
override open func accessibilityActivate() -> Bool {
sendActions(for: .touchUpInside)
return true
}
//--------------------------------------------------
// MARK: - Molecular
//--------------------------------------------------
open func needsToBeConstrained() -> Bool {
return true
}
open func reset() {
isEnabled(true)
shapeLayer?.removeAllAnimations()
shapeLayer?.removeFromSuperlayer()
shapeLayer = nil
backgroundColor = .clear
borderColor = .black
borderWidth = 1.0
checkColor = .black
checkWidth = 2.0
updateSelectionOnly = true
isSelected = false
updateSelectionOnly = false
}
open func setAsMolecule() {
setupView()
}
public func updateView(_ size: CGFloat) {
layoutIfNeeded()
}
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.delegateObject = delegateObject
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
guard let dictionary = json else { return }
groupName = dictionary.optionalStringForKey("groupName")
if let fieldKey = dictionary[KeyFieldKey] as? String {
self.fieldKey = fieldKey
}
if let isRequired = dictionary[KeyRequired] as? Bool {
self.isRequired = isRequired
}
if let borderColorHex = dictionary["borderColor"] as? String {
layer.borderColor = UIColor.mfGet(forHex: borderColorHex).cgColor
}
if let borderWidth = dictionary["borderWidth"] as? CGFloat {
layer.borderWidth = borderWidth
}
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
updateSelectionOnly = true
isSelected = isChecked
updateSelectionOnly = false
}
if let checkColorHex = dictionary["checkColor"] as? String {
checkColor = UIColor.mfGet(forHex: checkColorHex)
}
if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String {
unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex)
}
if let checkedBackgroundColorHex = dictionary["checkedBackgroundColor"] as? String {
checkedBackgroundColor = UIColor.mfGet(forHex: checkedBackgroundColorHex)
}
if let isAnimated = dictionary["isAnimated"] as? Bool {
self.isAnimated = isAnimated
}
if let isRound = dictionary["isRound"] as? Bool {
self.isRound = isRound
}
if let enabled = dictionary["isEnabled"] as? Bool {
isEnabled(enabled)
}
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
}
}
}
// MARK:- FormValidationProtocol
extension Checkbox: FormValidationFormFieldProtocol {
public func formFieldGroupName() -> String? {
return groupName
}
public func isValidField() -> Bool {
return isRequired ? isSelected : true
}
public func formFieldName() -> String? {
return fieldKey
}
public func formFieldValue() -> Any? {
return NSNumber(value: isSelected)
}
}

View File

@ -0,0 +1,200 @@
//
// CheckboxWithLabelView.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 9/13/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
@objcMembers open class CheckboxWithLabelView: ViewConstrainingView {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
public let checkbox = Checkbox()
public let label = Label.commonLabelB2(true)
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0)
var checkboxPosition: CheckboxPosition = .center
public enum CheckboxPosition: String {
case center
case top
case bottom
}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
var checkboxWidthConstraint: NSLayoutConstraint?
var checkboxHeightConstraint: NSLayoutConstraint?
var checkboxTopConstraint: NSLayoutConstraint?
var checkboxBottomConstraint: NSLayoutConstraint?
var checkboxCenterYConstraint: NSLayoutConstraint?
var centerLabelCheckboxConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Life Cycle
//--------------------------------------------------
override open func setupView() {
super.setupView()
guard subviews.isEmpty else { return }
translatesAutoresizingMaskIntoConstraints = false
addSubview(checkbox)
addSubview(label)
label.text = ""
let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth
checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension)
checkboxWidthConstraint?.isActive = true
checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension)
checkboxHeightConstraint?.isActive = true
checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true
let checboxBottom = checkbox.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
checboxBottom.priority = UILayoutPriority(249)
checboxBottom.isActive = true
// Allows various positions of checkbox.
checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor)
checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor)
centerLabelCheckboxConstraint = label.centerYAnchor.constraint(equalTo: checkbox.centerYAnchor)
label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true
let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor)
bottomLabelConstraint.priority = UILayoutPriority(249)
bottomLabelConstraint.isActive = true
alignCheckbox(.center)
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("xib file is not implemented for CheckboxWithLabelView")
}
override public init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
public convenience init() {
self.init(frame: .zero)
}
public convenience init(position: CheckboxPosition) {
self.init(frame: .zero)
alignCheckbox(position)
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Aligns Checkbox and Label relative to the desired position of the Checkbox.
private func alignCheckbox(_ position: CheckboxPosition) {
checkboxPosition = position
switch position {
case .center:
checkboxBottomConstraint?.isActive = false
checkboxTopConstraint?.isActive = false
checkboxCenterYConstraint?.isActive = true
centerLabelCheckboxConstraint?.isActive = true
case .top:
checkboxBottomConstraint?.isActive = false
checkboxTopConstraint?.isActive = true
checkboxCenterYConstraint?.isActive = false
centerLabelCheckboxConstraint?.isActive = false
case .bottom:
checkboxBottomConstraint?.isActive = true
checkboxTopConstraint?.isActive = false
checkboxCenterYConstraint?.isActive = false
centerLabelCheckboxConstraint?.isActive = false
}
}
}
/// MARK: - Molecular
extension CheckboxWithLabelView {
override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return CGFloat(Checkbox.defaultHeightWidth)
}
@objc override open func updateView(_ size: CGFloat) {
super.updateView(size)
label.updateView(size)
if self.checkbox.responds(to: #selector(self.updateView(_:))) {
if let dimension = sizeObject?.getValueBased(onSize: size) {
checkboxWidthConstraint?.constant = dimension
checkboxHeightConstraint?.constant = dimension
checkbox.updateView(size)
}
}
layoutIfNeeded()
}
override open func alignment() -> UIStackView.Alignment {
return .leading
}
open override func resetConstraints() {
super.resetConstraints()
checkboxCenterYConstraint?.isActive = false
checkboxBottomConstraint?.isActive = false
checkboxTopConstraint?.isActive = false
centerLabelCheckboxConstraint?.isActive = false
}
open override func reset() {
super.reset()
label.text = ""
checkbox.reset()
}
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json else { return }
if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) {
alignCheckbox(position)
}
checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData)
label.setWithJSON(dictionary.dictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData)
}
}

View File

@ -26,6 +26,9 @@ public typealias ActionBlock = () -> ()
public var sizeObject: MFSizeObject?
public var scaleSize: NSNumber?
/// A specific text index to use as a unique marker.
public var hero: Int?
// Used for scaling the font in updateView.
private var originalAttributedString: NSAttributedString?
@ -34,10 +37,15 @@ public typealias ActionBlock = () -> ()
return !text.isEmpty || !attributedText.string.isEmpty
}
public var getRange: NSRange {
return NSRange(location: 0, length: text?.count ?? 0)
}
//------------------------------------------------------
// MARK: - Multi-Action Text
//------------------------------------------------------
/// Data store of the tappable ranges of the text.
public var clauses: [ActionableClause] = [] {
didSet {
isUserInteractionEnabled = !clauses.isEmpty
@ -110,6 +118,15 @@ public typealias ActionBlock = () -> ()
standardFontSize = size
}
/// Convenience to init Label with a link comprised of range, actionMap and delegateObject
@objc convenience public init(text: String, range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
self.init()
self.text = text
if let actionBlock = createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) {
setTextLinkState(range: range, actionBlock: actionBlock)
}
}
//------------------------------------------------------
// MARK: - Factory Functions
//------------------------------------------------------
@ -305,8 +322,9 @@ public typealias ActionBlock = () -> ()
guard let actionLabel = label as? Label else { continue }
actionLabel.addActionAttributes(range: range, string: attributedString)
let actionBlock = actionLabel.createActionBlockFrom(actionMap: json, additionalData: additionalData, delegateObject: delegate)
actionLabel.appendActionableClause(range: range, actionBlock: actionBlock)
if let actionBlock = actionLabel.createActionBlockFor(actionMap: attribute, additionalData: additionalData, delegateObject: delegate) {
actionLabel.appendActionableClause(range: range, actionBlock: actionBlock)
}
default:
continue
@ -481,7 +499,7 @@ public typealias ActionBlock = () -> ()
}
/// Call to detect in the attributedText contains an NSTextAttachment.
func textContainsTextAttachment() -> Bool {
func containsTextAttachment() -> Bool {
guard let attributedText = attributedText else { return false }
@ -502,6 +520,50 @@ public typealias ActionBlock = () -> ()
let accessibleAction = customAccessibilityAction(range: range)
clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1))
}
/**
Provides a text container and layout manager of how the text would appear on screen.
They are used in tandem to derive low-level TextKit results of the label.
*/
public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? {
// Must configure the attributed string to translate what would appear on screen to accurately analyze.
guard let attributedText = attributedText else { return nil }
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = textAlignment
let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText)
stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count))
let textStorage = NSTextStorage(attributedString: stagedAttributedString)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
textContainer.size = bounds.size
return (textContainer, layoutManager, textStorage)
}
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
guard let abstractContainer = label.abstractTextContainer() else { return CGRect() }
let textContainer = abstractContainer.0
let layoutManager = abstractContainer.1
var glyphRange = NSRange()
// Convert the range for glyphs.
layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange)
return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
}
}
// MARK: - Atomization
@ -521,6 +583,8 @@ extension Label {
clauses = []
Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData)
originalAttributedString = attributedText
hero = json?["hero"] as? Int
}
public func setAsMolecule() {
@ -540,7 +604,7 @@ extension Label {
}
}
// MARK: - Multi-Action Functionality
// MARK: - Multi-Link Functionality
extension Label {
/// Applied to existing text. Removes underlines of tappable links and assoated actionable clauses.
@ -559,15 +623,17 @@ extension Label {
clauses = []
}
public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock {
public func createActionBlockFor(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? {
return { [weak self] in
if let wSelf = self, (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
guard let self = self else { return }
if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
}
}
func addActionAttributes(range: NSRange, string: NSMutableAttributedString?) {
private func addActionAttributes(range: NSRange, string: NSMutableAttributedString?) {
guard let string = string else { return }
string.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range)
@ -591,8 +657,7 @@ extension Label {
*/
@objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) {
setActionAttributes(range: range)
appendActionableClause(range: range, actionBlock: actionBlock)
setTextLinkState(range: range, actionBlock: actionBlock)
}
/**
@ -606,15 +671,35 @@ extension Label {
*/
@objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
if let actionBlock = createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) {
setTextLinkState(range: range, actionBlock: actionBlock)
}
}
/// Converts the entire text into a link. All characters will be underlined and the intrinsic bounds will respond to tap.
@objc public func makeTextButton(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
if let actionBlock = createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) {
setTextLinkState(range: getRange, actionBlock: actionBlock)
}
}
/// Converts the entire text into a link. All characters will be underlined and the intrinsic bounds will respond to tap.
@objc public func makeTextButton(actionBlock: @escaping ActionBlock) {
setTextLinkState(range: getRange, actionBlock: actionBlock)
}
/// Underlines the tappable region and stores the tap logic for interation.
private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) {
setActionAttributes(range: range)
let actionBlock = createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
appendActionableClause(range: range, actionBlock: actionBlock)
}
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
for clause in clauses {
// This determines if we tapped on the desired range of text.
if let range = clause.range, gesture.didTapAttributedTextInLabel(self, inRange: range) {
clause.performAction()
@ -634,30 +719,37 @@ extension UITapGestureRecognizer {
return true
}
// Must configure the attributed string to translate what would appear on screen to accurately analyze.
guard let attributedText = label.attributedText else { return false }
guard let abstractContainer = label.abstractTextContainer() else { return false }
let textContainer = abstractContainer.0
let layoutManager = abstractContainer.1
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = label.textAlignment
let tapLocation = location(in: label)
let indexOfGlyph = layoutManager.glyphIndex(for: tapLocation, in: textContainer)
let intrinsicWidth = label.intrinsicContentSize.width
let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText)
stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count))
// Assert that tapped occured within acceptable bounds based on alignment.
switch label.textAlignment {
case .right:
if tapLocation.x < label.bounds.width - intrinsicWidth {
return false
}
case .center:
let halfBounds = label.bounds.width / 2
let halfIntrinsicWidth = intrinsicWidth / 2
if tapLocation.x > halfBounds + halfIntrinsicWidth {
return false
} else if tapLocation.x < halfBounds - halfIntrinsicWidth {
return false
}
default: // Left align
if tapLocation.x > intrinsicWidth {
return false
}
}
let textStorage = NSTextStorage(attributedString: stagedAttributedString)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
textContainer.size = label.bounds.size
let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer)
return NSLocationInRange(indexOfGlyph, targetRange)
// Affirms that the tap occured in the desired rect of provided by the target range.
return layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer).contains(tapLocation) && NSLocationInRange(indexOfGlyph, targetRange)
}
}

View File

@ -207,7 +207,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
@objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
actionBlock = label?.createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
//------------------------------------------------------
@ -377,7 +377,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
actionText = actionMap?.optionalStringForKey(KeyTitle)
backText = actionMap?.optionalStringForKey(KeyTitlePostfix)
text = getTextFromStringComponents()
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
actionBlock = label?.createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
setLabelAttributes()
}

View File

@ -215,7 +215,7 @@ import UIKit
pinEdges(.all)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return json?.optionalCGFloatForKey("height") ?? 0
}

View File

@ -173,11 +173,10 @@
view.placeHolderLabel.textColor = [UIColor mfLightGrayColor];
// Disable SmartQuotes
if (@available(iOS 11.0, *)) {
view.textView.smartQuotesType = UITextSmartQuotesTypeNo;
view.textView.smartDashesType = UITextSmartDashesTypeNo;
view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
}
view.textView.smartQuotesType = UITextSmartQuotesTypeNo;
view.textView.smartDashesType = UITextSmartDashesTypeNo;
view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
[view didSetFont:view.textView.font];
view.hideBorder = YES;
return view;

View File

@ -48,9 +48,7 @@
- (void)setAsMolecule {
self.translatesAutoresizingMaskIntoConstraints = NO;
if (@available(iOS 11.0, *)) {
self.insetsLayoutMarginsFromSafeArea = NO;
}
self.insetsLayoutMarginsFromSafeArea = NO;
}
- (void)reset {

View File

@ -21,7 +21,7 @@
static const CGFloat FaultTolerance = 20.f;
static const CGFloat CheckBoxHeightWidth = 18.0;
@interface MVMCoreUICheckBox () <FormValidationProtocol, MVMCoreUIMoleculeViewProtocol>
@interface MVMCoreUICheckBox () <FormValidationFormFieldProtocol, MVMCoreUIMoleculeViewProtocol>
@property (nonatomic, readwrite) BOOL isSelected;
@property (weak, nonatomic) UIView *checkedSquare;
@ -42,7 +42,7 @@ static const CGFloat CheckBoxHeightWidth = 18.0;
@property (nonatomic) BOOL isRequired;
@property (nullable, strong, nonatomic) NSString *fieldKey;
@property (nullable, strong, nonatomic) DelegateObject *delegate;
@property (nullable, strong, nonatomic) MVMCoreUIDelegateObject *delegateObject;
@end
@ -61,7 +61,8 @@ static const CGFloat CheckBoxHeightWidth = 18.0;
- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData {
[FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol];
self.delegate = delegateObject;
self.delegateObject = delegateObject;
self.fieldKey = [json stringForKey:KeyFieldKey];
self.isRequired = [json boolForKey:KeyRequired];
@ -306,6 +307,8 @@ static const CGFloat CheckBoxHeightWidth = 18.0;
self.checkedSquare.layer.cornerRadius = self.isRoundRectCheckMark ? 5.0f : 0;
}
// TODO:.....................................
#pragma mark - XIB Helpers
- (instancetype)awakeAfterUsingCoder:(NSCoder *)aDecoder {
@ -345,10 +348,7 @@ static const CGFloat CheckBoxHeightWidth = 18.0;
[self.checkMark updateCheckSelected:NO animated:animated];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(formValidationProtocol)] && [[self.delegate performSelector:@selector(formValidationProtocol)] respondsToSelector:@selector(formValidatorModel)]) {
FormValidator *formValidator = [[self.delegate performSelector:@selector(formValidationProtocol)] performSelector:@selector(formValidatorModel)];
[formValidator enableByValidation];
}
[FormValidator enableByValidationWithDelegate:self.delegateObject.formValidationProtocol];
}
- (void)setColor:(nullable UIColor *)color forState:(UIControlState)state {

View File

@ -24,6 +24,7 @@ typedef void(^ValueChangeBlock)(void);
@property (nonatomic) BOOL shouldTouchToSwitch;
@property (nullable, copy, nonatomic) ValueChangeBlock valueChangedBlock;
@property (nullable, copy, nonatomic) ValueChangeBlock actionBlock;
+ (nonnull instancetype)mvmSwitchDefault;
+ (nonnull instancetype)mvmSwitchDefaultWithValueChangeBlock:(nullable ValueChangeBlock)block;

View File

@ -22,7 +22,7 @@ const CGFloat SwitchKnobWidth = 20;
const CGFloat SwitchKnobHeight = 20;
const CGFloat SwitchShakeIntensity = 2;
@interface MVMCoreUISwitch () <FormValidationProtocol, MVMCoreUIViewConstrainingProtocol>
@interface MVMCoreUISwitch () <FormValidationFormFieldProtocol, MVMCoreUIViewConstrainingProtocol>
@property (weak, nonatomic) UIView *baseView;
@property (weak, nonatomic) UIView *knobView;
@ -145,7 +145,8 @@ const CGFloat SwitchShakeIntensity = 2;
- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData {
self.json = json;
self.delegate = delegateObject;
[FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol];
NSString *color = [json string:@"onTintColor"];
@ -169,6 +170,17 @@ const CGFloat SwitchShakeIntensity = 2;
}
[self setState:[json boolForKey:@"state"] animated:false];
NSDictionary *actionMap = [json dict:@"actionMap"];
if (actionMap) {
[self addTarget:self action:@selector(addCustomAction) forControlEvents:UIControlEventTouchUpInside];
}
}
- (void)addCustomAction {
if (self.actionBlock) {
self.actionBlock();
}
}
+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject {

View File

@ -73,7 +73,7 @@ import Foundation
trackTintColor = UIColor.mfLightSilver()
}
public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 8
}
}

View File

@ -0,0 +1,135 @@
//
// RadioButton.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 4/25/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers public class RadioButton: Control, FormValidationFormFieldProtocol {
var diameter: CGFloat = 30 {
didSet {
widthConstraint?.constant = diameter
}
}
var enabledColor = UIColor.black
var disabledColor = UIColor.mfSilver()
var delegateObject: MVMCoreUIDelegateObject?
var fieldKey: String?
var formValue: Bool?
var isRequired: Bool = false
var widthConstraint: NSLayoutConstraint?
var heightConstraint: NSLayoutConstraint?
lazy var radioGroupName: String? = {
[unowned self] in
return json?.optionalStringForKey("radioGroupName") ?? json?.optionalStringForKey("fieldKey")
}()
lazy var radioButtonModel: RadioButtonModel? = {
[unowned self] in
if let radioGroupName = radioGroupName,
let radioButtonModel = delegateObject?.formValidationProtocol?.formValidatorModel?()?.radioButtonsModelByGroup[radioGroupName] {
return radioButtonModel
} else {
return nil
}
}()
open override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
let color = isEnabled ? enabledColor.cgColor : disabledColor.cgColor
layer.cornerRadius = bounds.width * 0.5
layer.borderColor = color
layer.borderWidth = bounds.width * 0.0333
if isSelected {
// Space around inner circle is 1/5 the size
context.addEllipse(in: CGRect(x: bounds.width*0.2, y: bounds.height*0.2, width: bounds.width*0.6, height: bounds.height*0.6))
context.setFillColor(color)
context.fillPath()
}
}
/// The action performed when tapped.
func tapAction() {
if let radioButtonModel = radioButtonModel {
radioButtonModel.selected(self)
} else {
isSelected = !isSelected
}
FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol)
setNeedsDisplay()
}
public func isValidField() -> Bool {
return isSelected
}
public func formFieldName() -> String? {
return json?.optionalStringForKey("fieldKey")
}
public func formFieldGroupName() -> String? {
return json?.optionalStringForKey("groupName")
}
public func formFieldValue() -> Any? {
return isSelected
}
// MARK: - MVMViewProtocol
open override func setupView() {
super.setupView()
backgroundColor = .white
clipsToBounds = true
widthConstraint = widthAnchor.constraint(equalToConstant: 30)
widthConstraint?.isActive = true
heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1)
heightConstraint?.isActive = true
addTarget(self, action: #selector(tapAction), for: .touchUpInside)
isAccessibilityElement = true
accessibilityTraits = .button
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint")
}
}
// MARK: - MVMCoreUIViewConstrainingProtocol
extension RadioButton: MVMCoreUIViewConstrainingProtocol {
public func needsToBeConstrained() -> Bool {
return true
}
}
// MARK: - MVMCoreUIMoleculeViewProtocol
extension RadioButton {
@objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
// Configure class properties with JSON values
guard let jsonDictionary = json else {
return
}
fieldKey = jsonDictionary.optionalStringForKey("fieldKey")
isRequired = jsonDictionary.boolForKey("required")
self.delegateObject = delegateObject
let radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self,
formValidator: delegateObject?.formValidationProtocol?.formValidatorModel?())
FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formValidationProtocol)
}
public override func reset() {
super.reset()
backgroundColor = .white
}
}

View File

@ -0,0 +1,62 @@
//
// Control.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 10/23/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
public class Control: UIControl {
var json: [AnyHashable: Any]?
private var initialSetupPerformed = false
public override init(frame: CGRect) {
super.init(frame: .zero)
initialSetup()
}
init() {
super.init(frame: .zero)
initialSetup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
initialSetup()
}
public func initialSetup() {
if !initialSetupPerformed {
initialSetupPerformed = true
setupView()
}
}
}
extension Control: MVMCoreViewProtocol {
public func updateView(_ size: CGFloat) {
}
/// Will be called only once.
public func setupView() {
translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false
}
}
extension Control: MVMCoreUIMoleculeViewProtocol {
public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
self.json = json
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
}
public func reset() {
backgroundColor = .clear
}
}

View File

@ -0,0 +1,61 @@
//
// View.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 10/23/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers open class View: UIView {
open var json: [AnyHashable: Any]?
private var initialSetupPerformed = false
public override init(frame: CGRect) {
super.init(frame: .zero)
initialSetup()
}
public init() {
super.init(frame: .zero)
initialSetup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
initialSetup()
}
public func initialSetup() {
if !initialSetupPerformed {
initialSetupPerformed = true
setupView()
}
}
}
extension View: MVMCoreViewProtocol {
open func updateView(_ size: CGFloat) {}
/// Will be called only once.
open func setupView() {
translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false
}
}
extension View: MVMCoreUIMoleculeViewProtocol {
open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
self.json = json
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
}
open func reset() {
backgroundColor = .clear
}
}

View File

@ -61,12 +61,8 @@
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.delegate = self;
tableView.dataSource = self;
if (@available(iOS 11.0, *)) {
tableView.insetsContentViewsToSafeArea = NO;
}
if ([tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) {
tableView.cellLayoutMarginsFollowReadableWidth = NO;
}
tableView.insetsContentViewsToSafeArea = NO;
tableView.cellLayoutMarginsFollowReadableWidth = NO;
return tableView;
}

View File

@ -91,10 +91,7 @@ static NSTimeInterval const HandScrollAnimationTiming = 7.f;
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
BOOL automaticInset = NO;
if (@available(iOS 11.0, *)) {
automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic;
}
BOOL automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic;
// Takes into account the navigation bar.
if (!automaticInset && (self.edgesForExtendedLayout & UIRectEdgeTop)) {

View File

@ -67,11 +67,13 @@
}
- (void)dismiss {
if (self.presentingViewController) {
[[MVMCoreNavigationHandler sharedNavigationHandler] dismissViewController:self animated:YES];
} else if (self.navigationController) {
[[MVMCoreNavigationHandler sharedNavigationHandler] popViewController:self animated:YES];
}
[MVMCoreDispatchUtility performBlockOnMainThread:^{
if (self.presentingViewController) {
[[MVMCoreNavigationHandler sharedNavigationHandler] dismissViewController:self animated:YES];
} else if (self.navigationController) {
[[MVMCoreNavigationHandler sharedNavigationHandler] popViewController:self animated:YES];
}
}];
}
- (BOOL)isVisibleViewController {
@ -520,6 +522,7 @@
// Don't track page state if there is a tab bar page control, it will be handled later.
if (!self.manager) {
[MVMCoreUISession sharedGlobal].currentPageType = self.pageType;
[self adobeTrackPageState];
}
@ -529,6 +532,13 @@
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.selectedField) {
[self.selectedField resignFirstResponder];
}
}
- (void)dealloc {
[self stopObservingForResponseJSONUpdates];
MVMCoreLog(@"%@ deallocated", [[self class] description]);
@ -814,6 +824,7 @@
if (self.initialLoadFinished) {
[self updateNavigationBarUI:self.manager.navigationController];
}
[MVMCoreUISession sharedGlobal].currentPageType = self.pageType;
[self adobeTrackPageState];
}

View File

@ -190,13 +190,9 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true
footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true
if #available(iOS 11.0, *) {
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
safeAreaView?.backgroundColor = bottomView?.backgroundColor
} else {
view.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
}
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
safeAreaView?.backgroundColor = bottomView?.backgroundColor
} else {
bottomConstraint?.isActive = true
var y: CGFloat?
@ -218,14 +214,16 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
/// Subclass for a top view.
open func viewForTop() -> UIView {
let view = MVMCoreUICommonViewsUtility.commonView()
view.heightAnchor.constraint(equalToConstant: 0).isActive = true
// Small height is needed to stop apple from adding padding for grouped tables when no header.
view.heightAnchor.constraint(equalToConstant: 1).isActive = true
return view
}
/// Subclass for a bottom view.
open func viewForBottom() -> UIView {
// Default spacing is standard when no buttons.
let view = MVMCoreUICommonViewsUtility.commonView()
view.heightAnchor.constraint(equalToConstant: 0).isActive = true
view.heightAnchor.constraint(equalToConstant: PaddingDefaultVerticalSpacing).isActive = true
return view
}

View File

@ -45,7 +45,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
return
}
if #available(iOS 11.0, *), scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic {
if scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic {
heightConstraint?.constant = -scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom
} else {
heightConstraint?.constant = -scrollView.contentInset.top - scrollView.contentInset.bottom
@ -233,14 +233,10 @@ extension ThreeLayerViewController {
view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
if #available(iOS 11.0, *) {
parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) {
safeAreaView.backgroundColor = bottomView?.backgroundColor
self.safeAreaView = safeAreaView
}
} else {
NSLayoutConstraint.pinViewBottom(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) {
safeAreaView.backgroundColor = bottomView?.backgroundColor
self.safeAreaView = safeAreaView
}
}
}

View File

@ -283,16 +283,6 @@
// So we will update titles.
[self newDataBuildScreen];
// Fix for right bar button item with custom view which disappears when user navigates to top tabbar page controller
if (@available(iOS 11.0, *)) {
} else {
NSMutableArray *buttonItems = [[NSMutableArray alloc] init];
UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
[buttonItems addObject:space];
[buttonItems addObjectsFromArray:self.navigationItem.rightBarButtonItems];
self.navigationItem.rightBarButtonItems = buttonItems;
}
}
- (void)viewWillDisappear:(BOOL)animated {

View File

@ -10,18 +10,32 @@ import Foundation
@objc public protocol FormValidationProtocol: NSObjectProtocol {
// Getter method to get the FormValidator form the delegate (Mostly from the parent View Controller)
// Getter method to get the FormValidator from the delegate (Mostly from the parent View Controller)
@objc optional func formValidatorModel() -> FormValidator?
}
@objc public protocol FormValidationFormFieldProtocol: FormValidationProtocol {
// Used to check the validity of the field. For example, to enable/disable the primary button.
@objc func isValidField() -> Bool
// Used to check the validity of the field, to enable/disable the primary button.
@objc optional func isValidField() -> Bool
// The Field name key value pair for sending to server
@objc func formFieldName() -> String?
// Returns the group name for validation. The class should always return a value.
@objc func formFieldGroupName() -> String?
// The Field value key value pair for sending to server
@objc func formFieldValue() -> Any?
}
@objc public protocol FormValidationEnableDisableProtocol: FormValidationProtocol {
// Returns true if the button needs to be enabled/disabled based on the validation
@objc func isValidationRequired() -> Bool
// Based on the isValidField(), the fields which needs to be enabled can call this method
@objc optional func enableField(_ enable: Bool)
// The Field name key value pair for sending to server
@objc optional func formFieldName() -> String?
// The Field value key value pair for sending to server
@objc optional func formFieldValue() -> Any?
// Returns the list of field keys required to enable/disable
@objc optional func requiredGroups() -> [String]?
}

View File

@ -16,13 +16,10 @@ import Foundation
@objc func getFormParams() -> [String: Any] {
var extraParam: [String: Any] = [:]
MVMCoreDispatchUtility.performSyncBlock(onMainThread: {
for molecule in self.molecules {
if let formFieldName = molecule.formFieldName,
let formFieldValue = molecule.formFieldValue,
let fieldName = formFieldName(),
let fieldValue = formFieldValue() {
extraParam[fieldName] = fieldValue
for molecule in self.fieldMolecules {
if let formFieldName = molecule.formFieldName(),
let formFieldValue = molecule.formFieldValue() {
extraParam[formFieldName] = formFieldValue
}
}
})

View File

@ -8,52 +8,79 @@
import Foundation
import UIKit
import MVMCore
@objcMembers public class FormValidator: NSObject {
var delegate: FormValidationProtocol?
var molecules: [UIView & FormValidationProtocol] = []
var extraValidationBlock: (() -> Bool)?
var dummyGroupName = "dummyGroupName"
weak var delegate: FormValidationProtocol?
var fieldMolecules: [FormValidationFormFieldProtocol] = []
var enableDisableMolecules: [FormValidationEnableDisableProtocol] = []
var radioButtonsModelByGroup: [String: RadioButtonModel] = [:]
public func insertMolecule(_ molecule: UIView & FormValidationProtocol) {
molecules.append(molecule)
public func insertMolecule(_ molecule: FormValidationProtocol) {
if let molecule = molecule as? FormValidationFormFieldProtocol {
fieldMolecules.append(molecule)
}
if let moleculeT = molecule as? FormValidationEnableDisableProtocol,
moleculeT.isValidationRequired() {
enableDisableMolecules.append(moleculeT)
}
}
public static func getFormValidatorFor(delegate: FormValidationProtocol) -> FormValidator? {
if let delegateFormValidatorModel = delegate.formValidatorModel,
let validator = delegateFormValidatorModel() {
return validator
} else {
return nil
public static func enableByValidationWith(delegate: FormValidationProtocol?) {
if let delegate = delegate {
let formValidator = FormValidator.getFormValidatorFor(delegate: delegate)
formValidator?.enableByValidation()
}
}
public static func setupValidation(molecule: UIView & FormValidationProtocol, delegate: FormValidationProtocol?) {
if let delegateFormValidatorModel = delegate?.formValidatorModel,
let validator = delegateFormValidatorModel() {
public static func getFormValidatorFor(delegate: FormValidationProtocol) -> FormValidator? {
return delegate.formValidatorModel?()
}
public static func setupValidation(molecule: FormValidationProtocol, delegate: FormValidationProtocol?) {
if let validator = delegate?.formValidatorModel?() {
validator.delegate = delegate
validator.insertMolecule(molecule)
}
}
public func enableByValidation() {
for molecule in enableDisableMolecules {
var requiredGroups = molecule.requiredGroups?() ?? [dummyGroupName]
if requiredGroups.count == 0 {
requiredGroups = [dummyGroupName]
}
enableWithGroups(requiredGroups, molecule)
}
}
public func enableWithGroups(_ requiredGroupList: [String], _ enableDisableMolecule: FormValidationEnableDisableProtocol) {
let requiredGroupSet = Set(requiredGroupList)
var valid = true
for molecule in molecules {
if let isValidField = molecule.isValidField,
isValidField() == false {
valid = false
for molecule in fieldMolecules {
let groupName = molecule.formFieldGroupName() ?? dummyGroupName
if requiredGroupSet.contains(groupName) {
valid = valid && molecule.isValidField()
if valid == false {
break
}
}
}
enableDisableMolecule.enableField?(valid)
}
public func enableIgnoreGroupName(_ enableDisableMolecules: FormValidationEnableDisableProtocol) {
var valid = true
for molecule in fieldMolecules {
valid = valid && molecule.isValidField()
if (!valid) {
break
}
}
let enableField = valid && (extraValidationBlock?() ?? true)
shouldEnable(enableField)
}
public func shouldEnable(_ enable: Bool) {
for molecule in molecules {
if let enableField = molecule.enableField {
enableField(enable)
}
}
enableDisableMolecules.enableField?(enableField)
}
}

View File

@ -325,19 +325,13 @@
NSLayoutConstraint *bottomViewTop = [NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:tableView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
bottomViewTop.active = YES;
NSLayoutConstraint *bottomViewBot = nil;
if (@available(iOS 11.0, *)) {
bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor];
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
safeAreaView.backgroundColor = footerView.backgroundColor;
self.safeAreaView = safeAreaView;
} else {
// Fallback on earlier versions
bottomViewBot = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:footerView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
}
bottomViewBot.priority = 900;
bottomViewBot.active = YES;
NSLayoutConstraint *bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor];
bottomViewBot.priority = 900;
bottomViewBot.active = YES;
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
safeAreaView.backgroundColor = self.bottomView.backgroundColor;
self.safeAreaView = safeAreaView;
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]];
} else {

View File

@ -272,13 +272,8 @@
- (void)updateViewConstraints {
[super updateViewConstraints];
// Updates for ios 11
if (@available(iOS 11.0, *)) {
if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom;
} else {
self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom;
}
if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom;
} else {
self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom;
}
@ -322,17 +317,13 @@
[self.view addSubview:bottomView];
UIScrollView *scrollview = self.scrollView;
if (@available(iOS 11.0, *)) {
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
[self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES;
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
safeAreaView.backgroundColor = bottomView.backgroundColor;
self.safeAreaView = safeAreaView;
} else {
// Fallback on earlier versions
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
}
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
[self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES;
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
safeAreaView.backgroundColor = bottomView.backgroundColor;
self.safeAreaView = safeAreaView;
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[bottomView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(bottomView)]];
}

View File

@ -14,10 +14,8 @@ import UIKit
// MARK: - Outlets
//------------------------------------------------------
let header = HeadlineBody(frame: .zero)
let button = PrimaryButton.primaryTinyButton(false)!
let headlineBodyButton = HeadlineBodyButton(frame: .zero)
let imageLoader = MFLoadImageView(pinnedEdges: .all)
let leftContainer = ViewConstrainingView.empty()
//------------------------------------------------------
// MARK: - Properties
@ -30,7 +28,6 @@ import UIKit
//------------------------------------------------------
var imageLeadingConstraint: NSLayoutConstraint?
var buttonTopConstraint: NSLayoutConstraint?
//------------------------------------------------------
// MARK: - Initialization
@ -64,33 +61,20 @@ import UIKit
setDefaultState()
addSubview(leftContainer)
addSubview(imageLoader)
leftContainer.addSubview(header)
leftContainer.addSubview(button)
addSubview(headlineBodyButton)
leftContainer.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
leftContainer.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
headlineBodyButton.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
headlineBodyButton.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: leftContainer.bottomAnchor).isActive = true
let leftContainerBottom = leftContainer.bottomAnchor.constraint(equalTo: bottomAnchor)
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: headlineBodyButton.bottomAnchor).isActive = true
let leftContainerBottom = headlineBodyButton.bottomAnchor.constraint(equalTo: bottomAnchor)
leftContainerBottom.priority = UILayoutPriority(249)
leftContainerBottom.isActive = true
header.topAnchor.constraint(equalTo: leftContainer.topAnchor).isActive = true
header.leadingAnchor.constraint(equalTo: leftContainer.leadingAnchor).isActive = true
leftContainer.trailingAnchor.constraint(equalTo: header.trailingAnchor).isActive = true
buttonTopConstraint = button.topAnchor.constraint(equalTo: header.bottomAnchor, constant: buttonHeaderPadding)
buttonTopConstraint?.isActive = true
button.leadingAnchor.constraint(equalTo: leftContainer.leadingAnchor).isActive = true
leftContainer.bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true
leftContainer.trailingAnchor.constraint(greaterThanOrEqualTo: button.trailingAnchor).isActive = true
imageLoader.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
layoutMarginsGuide.trailingAnchor.constraint(equalTo: imageLoader.trailingAnchor).isActive = true
imageLeadingConstraint = imageLoader.leadingAnchor.constraint(greaterThanOrEqualTo: leftContainer.trailingAnchor, constant: 16)
imageLeadingConstraint = imageLoader.leadingAnchor.constraint(greaterThanOrEqualTo: headlineBodyButton.trailingAnchor, constant: 16)
imageLeadingConstraint?.isActive = true
imageLoader.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true
@ -103,15 +87,11 @@ import UIKit
override open func updateView(_ size: CGFloat) {
super.updateView(size)
header.updateView(size)
button.updateView(size)
headlineBodyButton.updateView(size)
imageLoader.updateView(size)
leftContainer.updateView(size)
buttonTopConstraint?.constant = header.hasText() ? buttonHeaderPadding : 0
}
public override static func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 197
}
@ -121,10 +101,8 @@ import UIKit
private func setDefaultState() {
header.headlineLabel.font = MFStyler.fontH3()
header.messageLabel.font = MFStyler.fontB3()
button.setAsSecondaryCustom()
button.isHidden = false
headlineBodyButton.headlineBody.headlineLabel.font = MFStyler.fontH3()
headlineBodyButton.headlineBody.messageLabel.font = MFStyler.fontB3()
imageLoader.imageView.contentMode = .scaleAspectFit
imageLoader.addSizeConstraintsForAspectRatio = true
buttonHeaderPadding = PaddingTwo
@ -133,8 +111,7 @@ import UIKit
override open func reset() {
super.reset()
header.reset()
button.reset()
headlineBodyButton.reset()
imageLeadingConstraint?.constant = 16
imageLoader.reset()
setDefaultState()
@ -143,8 +120,7 @@ import UIKit
open override func setAsMolecule() {
super.setAsMolecule()
header.setAsMolecule()
button.setAsMolecule()
headlineBodyButton.setAsMolecule()
imageLoader.setAsMolecule()
setDefaultState()
}
@ -153,18 +129,8 @@ import UIKit
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json else { return }
if let padding = dictionary.optionalCGFloatForKey("buttonHeaderPadding") {
buttonHeaderPadding = padding
}
header.setWithJSON(dictionary.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData)
headlineBodyButton.setWithJSON(dictionary.optionalDictionaryForKey("headlineBodyButton"), delegateObject: delegateObject, additionalData: additionalData)
imageLoader.setWithJSON(dictionary.optionalDictionaryForKey("image"), delegateObject: delegateObject, additionalData: additionalData)
if let buttonDictionary = dictionary.optionalDictionaryForKey("button") {
button.setWithJSON(buttonDictionary, delegateObject: delegateObject, additionalData: additionalData)
} else {
button.isHidden = true
}
}
}

View File

@ -0,0 +1,135 @@
//
// HeadlineBodyButton.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 9/12/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers open class HeadlineBodyButton: ViewConstrainingView {
//------------------------------------------------------
// MARK: - Outlets
//------------------------------------------------------
let headlineBody = HeadlineBody(frame: .zero)
let button = PrimaryButton.primaryTinyButton(false)!
//------------------------------------------------------
// MARK: - Properties
//------------------------------------------------------
var buttonHeadlinePadding: CGFloat = 16
//------------------------------------------------------
// MARK: - Constraints
//------------------------------------------------------
var buttonTopConstraint: NSLayoutConstraint?
//------------------------------------------------------
// MARK: - Initialization
//------------------------------------------------------
public init() {
super.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: frame)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public convenience init(json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.init()
setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
}
//------------------------------------------------------
// MARK: - View Lifecycle
//------------------------------------------------------
override open func setupView() {
super.setupView()
guard subviews.isEmpty else { return }
defaultState()
addSubview(headlineBody)
addSubview(button)
headlineBody.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
headlineBody.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
layoutMarginsGuide.trailingAnchor.constraint(equalTo: headlineBody.trailingAnchor).isActive = true
buttonTopConstraint = button.topAnchor.constraint(equalTo: headlineBody.bottomAnchor, constant: buttonHeadlinePadding)
buttonTopConstraint?.isActive = true
button.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
layoutMarginsGuide.bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true
layoutMarginsGuide.trailingAnchor.constraint(greaterThanOrEqualTo: button.trailingAnchor).isActive = true
}
override open func updateView(_ size: CGFloat) {
super.updateView(size)
headlineBody.updateView(size)
button.updateView(size)
buttonTopConstraint?.constant = headlineBody.hasText() && !button.isHidden ? buttonHeadlinePadding : 0
}
private func defaultState() {
headlineBody.headlineLabel.font = MFStyler.fontH3()
headlineBody.messageLabel.font = MFStyler.fontB3()
button.setAsTiny(true)
button.setAsSecondaryCustom()
button.isHidden = false
buttonHeadlinePadding = PaddingTwo
}
//------------------------------------------------------
// MARK: - Molecule
//------------------------------------------------------
override open func reset() {
super.reset()
headlineBody.reset()
button.reset()
defaultState()
}
open override func setAsMolecule() {
super.setAsMolecule()
headlineBody.setAsMolecule()
button.setAsMolecule()
defaultState()
}
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json else { return }
if let padding = dictionary.optionalCGFloatForKey("buttonHeadlinePadding") {
buttonHeadlinePadding = padding
}
headlineBody.setWithJSON(dictionary.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData)
if let buttonDictionary = dictionary.optionalDictionaryForKey("button") {
button.setWithJSON(buttonDictionary, delegateObject: delegateObject, additionalData: additionalData)
} else {
button.isHidden = true
}
}
}

View File

@ -82,7 +82,7 @@ import UIKit
imageView.reset()
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 95
}
}

View File

@ -37,11 +37,9 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi
}
isAccessibilityElement = false
contentView.isAccessibilityElement = false
if #available(iOS 11.0, *) {
insetsLayoutMarginsFromSafeArea = false
contentView.insetsLayoutMarginsFromSafeArea = false
contentView.preservesSuperviewLayoutMargins = false
}
insetsLayoutMarginsFromSafeArea = false
contentView.insetsLayoutMarginsFromSafeArea = false
contentView.preservesSuperviewLayoutMargins = false
// Covers the card when peaking.
peakingCover.backgroundColor = .white
@ -117,7 +115,7 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi
backgroundColor = .white
}
public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
public class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else {
return nil
}

View File

@ -8,195 +8,24 @@
import UIKit
@objcMembers open class MoleculeTableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol {
open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)?
open var json: [AnyHashable: Any]?
// In updateView, will set padding to default.
open var updateViewHorizontalDefaults = true
// For the accessory view convenience.
public var caretView: CaretView?
private var caretViewWidthSizeObject: MFSizeObject?
private var caretViewHeightSizeObject: MFSizeObject?
// For separation between cells.
public var topSeparatorView: SeparatorView?
public var bottomSeparatorView: SeparatorView?
public enum SeparatorFrequency: String {
case All = "all"
case AllExceptTop = "allExceptTop"
case AllExceptBottom = "allExceptBottom"
case Between = "between"
}
/// For subclasses that want to use a custom accessory view.
open var customAccessoryView = false
public var topMarginPadding: CGFloat = 24
public var bottomMarginPadding: CGFloat = 24
// MARK: - Styling
func style(with styleString: String?) {
guard let styleString = styleString else {
return
}
switch styleString {
case "standard":
styleStandard()
case "header":
styleHeader()
case "none":
styleNone()
default: break
}
}
func styleStandard() {
topMarginPadding = 24
bottomMarginPadding = 24
bottomSeparatorView?.show()
bottomSeparatorView?.setAsLight()
}
func styleHeader() {
topMarginPadding = 48
bottomMarginPadding = 16
bottomSeparatorView?.show()
bottomSeparatorView?.setAsRegular()
}
func styleNone() {
topMarginPadding = 0
bottomMarginPadding = 0
bottomSeparatorView?.hide()
}
// MARK: - Inits
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
// MARK: - MFViewProtocol
public func updateView(_ size: CGFloat) {
MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding)
if #available(iOS 11.0, *) {
if accessoryView != nil {
// Smaller left margin if accessory view.
var margin = directionalLayoutMargins
margin.trailing = 16
contentView.directionalLayoutMargins = margin
} else {
contentView.directionalLayoutMargins = directionalLayoutMargins
}
topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
} else {
if accessoryView != nil {
// Smaller left margin if accessory view.
var margin = layoutMargins
margin.right = 16
contentView.layoutMargins = margin
} else {
contentView.layoutMargins = layoutMargins
}
topSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left)
bottomSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left)
}
molecule?.updateView(size)
if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject {
caretView.frame = CGRect(x: 0, y: 0, width: widthObject.getValueBased(onSize: size), height: heightObject.getValueBased(onSize: size))
}
topSeparatorView?.updateView(size)
bottomSeparatorView?.updateView(size)
}
public func setupView() {
selectionStyle = .none
if #available(iOS 11.0, *) {
insetsLayoutMarginsFromSafeArea = false
contentView.insetsLayoutMarginsFromSafeArea = false
contentView.preservesSuperviewLayoutMargins = false
}
}
@objcMembers open class MoleculeTableViewCell: TableViewCell {
// MARK: - MVMCoreUIMoleculeViewProtocol
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.json = json;
public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
style(with: json?.optionalStringForKey("style"))
if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") {
updateViewHorizontalDefaults = useHorizontalMargins
}
if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false {
topMarginPadding = 0
bottomMarginPadding = 0
}
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
// Add the caret if there is an action and it's not declared hidden.
if !customAccessoryView {
if let _ = json?.optionalDictionaryForKey("actionMap"), !json!.boolForKey("hideArrow") {
addCaretViewAccessory()
} else {
accessoryView = nil
}
}
// override the separator
if let separator = json?.optionalDictionaryForKey("separator") {
addSeparatorsIfNeeded()
bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData)
}
guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else {
return
}
if molecule == nil {
if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) {
contentView.addSubview(moleculeView)
let standardConstraints = (moleculeView as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values))
molecule = moleculeView
}
} else {
molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
}
// This molecule will by default handle margins.
if let castView = molecule as? MVMCoreUIViewConstrainingProtocol {
castView.shouldSetHorizontalMargins?(false)
castView.shouldSetVerticalMargins?(false)
}
guard molecule == nil, let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule), let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) else { return }
addMolecule(moleculeView)
}
public func reset() {
molecule?.reset?()
updateViewHorizontalDefaults = true
styleStandard()
backgroundColor = .white
}
public static func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) else {
return 80
}
return max(2 * PaddingDefaultVerticalSpacing3, height)
}
public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
public override class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else {
return "\(self)<>"
}
@ -204,89 +33,11 @@ import UIKit
return "\(self)<\(moleculeName)>"
}
public static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule),
let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON) else {
return nil
}
return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error)
}
// MARK: - Arrow
/// Adds the standard mvm style caret to the accessory view
public func addCaretViewAccessory() {
guard accessoryView == nil else {
return
}
let width: CGFloat = 6
let height: CGFloat = 10
caretView = CaretView(lineThickness: CaretView.thin)
caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height)
caretViewWidthSizeObject = MFSizeObject(standardSize: width, standardiPadPortraitSize: 9)
caretViewHeightSizeObject = MFSizeObject(standardSize: height, standardiPadPortraitSize: 16)
accessoryView = caretView
}
// MARK: - MoleculeListCellProtocol
/// For when the separator between cells shows using json and frequency. Default is type: standard, frequency: allExceptTop.
public func setSeparatorWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) {
addSeparatorsIfNeeded()
if let json = json {
topSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
bottomSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
if let separatorFrequencyString = json.optionalStringForKey("frequency"), let separatorFrequency = SeparatorFrequency(rawValue: separatorFrequencyString) {
setSeparatorFrequency(separatorFrequency, indexPath: indexPath)
}
} else {
topSeparatorView?.hide()
bottomSeparatorView?.setAsLight()
setSeparatorFrequency(MoleculeTableViewCell.SeparatorFrequency.AllExceptTop, indexPath: indexPath)
}
}
public func didSelectCell(atIndex indexPath: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
if let actionMap = json?.optionalDictionaryForKey("actionMap") {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
}
// MARK: - Separator
func addSeparatorsIfNeeded() {
if topSeparatorView == nil {
topSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionTop)
topSeparatorView?.hide()
}
if bottomSeparatorView == nil {
bottomSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot)
bottomSeparatorView?.hide()
}
}
/// For when the separator between cells shows.
public func setSeparatorFrequency(_ separatorFrequency: SeparatorFrequency, indexPath: IndexPath) {
switch separatorFrequency {
case .All:
if indexPath.row == 0 {
topSeparatorView?.show()
} else {
topSeparatorView?.hide()
}
bottomSeparatorView?.show()
case .AllExceptBottom:
topSeparatorView?.show()
bottomSeparatorView?.hide()
case .Between:
if indexPath.row == 0 {
topSeparatorView?.hide()
} else {
topSeparatorView?.show()
}
bottomSeparatorView?.hide()
case .AllExceptTop:
fallthrough
default:
topSeparatorView?.hide()
bottomSeparatorView?.show()
}
}
}

View File

@ -0,0 +1,314 @@
//
// TableViewCell.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 10/29/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers open class TableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol {
open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)?
open var json: [AnyHashable: Any]?
// In updateView, will set padding to default.
open var updateViewHorizontalDefaults = true
// For the accessory view convenience.
private var caretView: CaretView?
private var caretViewWidthSizeObject: MFSizeObject?
private var caretViewHeightSizeObject: MFSizeObject?
// For separation between cells.
public var topSeparatorView: SeparatorView?
public var bottomSeparatorView: SeparatorView?
public enum SeparatorFrequency: String {
case all
case allExceptTop
case allExceptBottom
case between
}
/// For subclasses that want to use a custom accessory view.
open var customAccessoryView = false
open var topMarginPadding: CGFloat = 24
open var bottomMarginPadding: CGFloat = 24
private var heroAccessoryCenter: CGPoint?
// MARK: - Styling
open func style(with styleString: String?) {
guard let styleString = styleString else {
return
}
switch styleString {
case "standard":
styleStandard()
case "header":
styleHeader()
case "none":
styleNone()
default: break
}
}
open func styleStandard() {
topMarginPadding = 24
bottomMarginPadding = 24
bottomSeparatorView?.show()
bottomSeparatorView?.setAsLight()
}
open func styleHeader() {
topMarginPadding = 48
bottomMarginPadding = 16
bottomSeparatorView?.show()
bottomSeparatorView?.setAsRegular()
}
open func styleNone() {
topMarginPadding = 0
bottomMarginPadding = 0
bottomSeparatorView?.hide()
}
/// Adds the molecule to the view.
open func addMolecule(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) {
contentView.addSubview(molecule)
let standardConstraints = (molecule as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: molecule, useMargins: standardConstraints).values))
// This molecule will by default handle margins.
if let castView = molecule as? MVMCoreUIViewConstrainingProtocol {
castView.shouldSetHorizontalMargins?(false)
castView.shouldSetVerticalMargins?(false)
}
self.molecule = molecule
}
open override func layoutSubviews() {
super.layoutSubviews()
// Ensures accessory view aligns to the center y derived from the
if let center = heroAccessoryCenter {
accessoryView?.center.y = center.y
}
}
// MARK: - Inits
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
// MARK: - MFViewProtocol
public func updateView(_ size: CGFloat) {
MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding)
if accessoryView != nil {
// Smaller left margin if accessory view.
var margin = directionalLayoutMargins
margin.trailing = 16
contentView.directionalLayoutMargins = margin
} else {
contentView.directionalLayoutMargins = directionalLayoutMargins
}
topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
molecule?.updateView(size)
if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject {
caretView.frame = CGRect(x: 0, y: 0, width: widthObject.getValueBased(onSize: size), height: heightObject.getValueBased(onSize: size))
}
topSeparatorView?.updateView(size)
bottomSeparatorView?.updateView(size)
}
public func setupView() {
selectionStyle = .none
insetsLayoutMarginsFromSafeArea = false
contentView.insetsLayoutMarginsFromSafeArea = false
contentView.preservesSuperviewLayoutMargins = false
}
// MARK: - MVMCoreUIMoleculeViewProtocol
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.json = json
guard let json = json else { return }
style(with: json.optionalStringForKey("style"))
if let useHorizontalMargins = json.optionalBoolForKey("useHorizontalMargins") {
updateViewHorizontalDefaults = useHorizontalMargins
}
if (json.optionalBoolForKey("useVerticalMargins") ?? true) == false {
topMarginPadding = 0
bottomMarginPadding = 0
}
if let backgroundColorString = json.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
// Add the caret if there is an action and it's not declared hidden.
if !customAccessoryView {
if let _ = json.optionalDictionaryForKey("actionMap"), !json.boolForKey("hideArrow") {
addCaretViewAccessory()
} else {
accessoryView = nil
}
}
// override the separator
if let separator = json.optionalDictionaryForKey("separator") {
addSeparatorsIfNeeded()
bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData)
}
guard let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return }
molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
// This molecule will by default handle margins.
if let castView = molecule as? MVMCoreUIViewConstrainingProtocol {
castView.shouldSetHorizontalMargins?(false)
castView.shouldSetVerticalMargins?(false)
}
}
public func reset() {
molecule?.reset?()
updateViewHorizontalDefaults = true
styleStandard()
backgroundColor = .white
}
public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) else {
return 80
}
return max(2 * PaddingDefaultVerticalSpacing3, height)
}
public class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
return molecule?.optionalStringForKey(KeyMoleculeName) ?? ""
}
// MARK: - Arrow
/// Adds the standard mvm style caret to the accessory view
@objc public func addCaretViewAccessory() {
guard accessoryView == nil else { return }
let width: CGFloat = 6
let height: CGFloat = 10
caretView = CaretView(lineThickness: CaretView.thin)
caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height)
caretViewWidthSizeObject = MFSizeObject(standardSize: width, standardiPadPortraitSize: 9)
caretViewHeightSizeObject = MFSizeObject(standardSize: height, standardiPadPortraitSize: 16)
accessoryView = caretView
}
/// NOTE: Should only be called when displayed or about to be displayed.
public func alignAccessoryToHero() {
// Layout call required to force draw in memory to get dimensions of subviews.
layoutIfNeeded()
guard let heroLabel = findHeroLabel(views: contentView.subviews), let hero = heroLabel.hero else { return }
let rect = Label.boundingRect(forCharacterRange: NSRange(location: hero, length: 1), in: heroLabel)
accessoryView?.center.y = contentView.convert(UIView(frame: rect).center, from: heroLabel).y
heroAccessoryCenter = accessoryView?.center
}
/// Traverses the view hierarchy for a 🦸heroic Label.
private func findHeroLabel(views: [UIView]) -> Label? {
if views.isEmpty {
return nil
}
var queue = [UIView]()
for view in views {
// Only one Label will have a hero in a table cell.
if let label = view as? Label, label.hero != nil {
return label
}
queue.append(contentsOf: view.subviews)
}
return findHeroLabel(views: queue)
}
// MARK: - MoleculeListCellProtocol
/// For when the separator between cells shows using json and frequency. Default is type: standard, frequency: allExceptTop.
public func setSeparatorWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) {
addSeparatorsIfNeeded()
if let json = json {
topSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
bottomSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
if let separatorFrequencyString = json.optionalStringForKey("frequency"), let separatorFrequency = SeparatorFrequency(rawValue: separatorFrequencyString) {
setSeparatorFrequency(separatorFrequency, indexPath: indexPath)
}
} else {
topSeparatorView?.hide()
bottomSeparatorView?.setAsLight()
setSeparatorFrequency(TableViewCell.SeparatorFrequency.allExceptTop, indexPath: indexPath)
}
}
public func didSelectCell(atIndex indexPath: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
if let actionMap = json?.optionalDictionaryForKey("actionMap") {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
}
public func willDisplay() {
alignAccessoryToHero()
}
// MARK: - Separator
open func addSeparatorsIfNeeded() {
if topSeparatorView == nil {
topSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionTop)
topSeparatorView?.hide()
}
if bottomSeparatorView == nil {
bottomSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot)
bottomSeparatorView?.hide()
}
}
/// For when the separator between cells shows.
public func setSeparatorFrequency(_ separatorFrequency: SeparatorFrequency, indexPath: IndexPath) {
switch separatorFrequency {
case .all:
if indexPath.row == 0 {
topSeparatorView?.show()
} else {
topSeparatorView?.hide()
}
bottomSeparatorView?.show()
case .allExceptBottom:
topSeparatorView?.show()
bottomSeparatorView?.hide()
case .between:
if indexPath.row == 0 {
topSeparatorView?.hide()
} else {
topSeparatorView?.show()
}
bottomSeparatorView?.hide()
case .allExceptTop:
fallthrough
default:
topSeparatorView?.hide()
bottomSeparatorView?.show()
}
}
}

View File

@ -8,7 +8,7 @@
import UIKit
@objcMembers public class TabsTableViewCell: MoleculeTableViewCell {
@objcMembers public class TabsTableViewCell: TableViewCell {
let tabs = TopTabbar(frame: .zero)
var delegateObject: MVMCoreUIDelegateObject?
var previousTabIndex = 0

View File

@ -174,7 +174,7 @@ import UIKit
bottomRightLabel.styleB3(true)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 34
}
}

View File

@ -8,9 +8,9 @@
import UIKit
@objcMembers public class HeadlineBodySwitch: ViewConstrainingView {
let headlineBody = HeadlineBody(frame: .zero)
let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault()
@objcMembers open class HeadlineBodySwitch: ViewConstrainingView {
public let headlineBody = HeadlineBody(frame: .zero)
public let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault()
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) {
@ -19,7 +19,7 @@ import UIKit
mvmSwitch.updateView(size)
}
public override func setupView() {
open override func setupView() {
super.setupView()
guard mvmSwitch.superview == nil else {
return
@ -35,24 +35,24 @@ import UIKit
}
// MARK: - MVMCoreUIMoleculeViewProtocol
public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
headlineBody.setWithJSON(json?.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData)
mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
open override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 30
}
public override func setAsMolecule() {
open override func setAsMolecule() {
super.setAsMolecule()
headlineBody.setAsMolecule()
(mvmSwitch as MVMCoreUIMoleculeViewProtocol).setAsMolecule?()
headlineBody.styleListItem()
}
public override func reset() {
open override func reset() {
super.reset()
headlineBody.reset()
(mvmSwitch as MVMCoreUIMoleculeViewProtocol).reset?()

View File

@ -41,7 +41,7 @@ import UIKit
mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return HeadlineBodyTextButton.estimatedHeight(forRow: json, delegateObject: delegateObject)
}

View File

@ -41,7 +41,7 @@ import UIKit
mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return MVMCoreUISwitch.estimatedHeight(forRow: json, delegateObject: delegateObject)
}

View File

@ -31,7 +31,6 @@
/// Resets to default state before set with json is called again.
- (void)reset;
/// For the molecule list to load more efficiently.
+ (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject;

View File

@ -54,7 +54,7 @@ open class ModuleMolecule: ViewConstrainingView {
moduleMolecule?.reset?()
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
guard let moduleName = json?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
// Critical error
return 0
@ -62,7 +62,7 @@ open class ModuleMolecule: ViewConstrainingView {
return MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.estimatedHeight?(forRow: module, delegateObject: delegateObject) ?? 0
}
public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
guard let moduleName = molecule?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
// Critical error
return "moduleMolecule<>"
@ -70,7 +70,7 @@ open class ModuleMolecule: ViewConstrainingView {
return "moduleMolecule<" + (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.name?(forReuse: module, delegateObject: delegateObject) ?? module.stringForkey(KeyMoleculeName)) + ">"
}
public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
let moduleName = json?.optionalStringForKey("moduleName")
if moduleName == nil || delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) == nil {
if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) {

View File

@ -0,0 +1,90 @@
//
// RadioButtonWithLabel.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 10/21/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers public class RadioButtonLabel: ViewConstrainingView {
public let radioButton = RadioButton()
var delegateObject: MVMCoreUIDelegateObject?
let label = Label()
// MARK: - Inits
public init() {
super.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: frame)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public override func updateView(_ size: CGFloat) {
super.updateView(size)
radioButton.updateView(size)
label.updateView(size)
}
open override func setupView() {
super.setupView()
guard subviews.count == 0 else {
return
}
addSubview(radioButton)
radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true
radioButton.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor, constant: PaddingOne).isActive = true
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true
radioButton.centerYAnchor.constraint(equalTo: layoutMarginsGuide.centerYAnchor).isActive = true
if let rightView = createRightView() {
addSubview(rightView)
rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true
rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true
var constraint = rightView.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor, constant: PaddingOne)
constraint.priority = .defaultHigh
constraint.isActive = true
constraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: rightView.bottomAnchor, constant: PaddingOne)
constraint.priority = .defaultHigh
constraint.isActive = true
layoutMarginsGuide.centerYAnchor.constraint(equalTo: rightView.centerYAnchor).isActive = true
}
}
func createRightView() -> ViewConstrainingView? {
let rightView = ViewConstrainingView(constrainingView: label)
return rightView
}
}
// MARK: - MVMCoreUIMoleculeViewProtocol
extension RadioButtonLabel {
@objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
self.delegateObject = delegateObject
radioButton.setWithJSON(json?.optionalDictionaryForKey("radioButton"), delegateObject: delegateObject, additionalData: additionalData)
label.setWithJSON(json?.optionalDictionaryForKey(KeyLabel),
delegateObject: delegateObject,
additionalData: additionalData)
}
public override func reset() {
super.reset()
radioButton.reset()
label.reset()
}
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 45
}
}

View File

@ -0,0 +1,54 @@
//
// RadioButtonModel.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 5/14/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import Foundation
import UIKit
@objcMembers public class RadioButtonModel: NSObject {
private var selectedRadioButton: RadioButton?
private var fieldGroupName: String?
public static func setupForRadioButtonGroup(radioButton: RadioButton, formValidator: FormValidator?) -> RadioButtonModel {
guard let groupName = radioButton.radioGroupName,
let formValidator = formValidator else {
return RadioButtonModel()
}
let radioButtonModel = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonModel()
radioButtonModel.fieldGroupName = radioButton.formFieldGroupName()
formValidator.radioButtonsModelByGroup[groupName] = radioButtonModel
return radioButtonModel
}
public func selected(_ radioButton: RadioButton) {
selectedRadioButton?.isSelected = false
selectedRadioButton = radioButton
selectedRadioButton?.isSelected = true
}
}
// MARK: - FormValidationFormFieldProtocol
extension RadioButtonModel: FormValidationFormFieldProtocol {
public func formFieldGroupName() -> String? {
return selectedRadioButton?.formFieldGroupName() ?? self.fieldGroupName
}
// Used to check the validity of the field, to enable/disable the primary button.
@objc public func isValidField() -> Bool {
return selectedRadioButton != nil ? true : false
}
// Name of the field to send to server
@objc public func formFieldName() -> String? {
return selectedRadioButton?.fieldKey
}
// The field value key value pair for sending to server
@objc public func formFieldValue() -> Any? {
return selectedRadioButton != nil ? true : false
}
}

View File

@ -26,7 +26,7 @@ open class StandardFooterView: ViewConstrainingView {
(molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) {
return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing
}

View File

@ -80,7 +80,7 @@ public class StandardHeaderView: ViewConstrainingView, ModelMoleculeViewProtocol
separatorView?.show()
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) {
return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing
}

View File

@ -64,7 +64,7 @@ import UIKit
body.styleB2(true)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 65
}
}

View File

@ -136,7 +136,7 @@ open class HeadlineBody: ViewConstrainingView {
stylePageHeader()
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 58
}
}

View File

@ -76,7 +76,7 @@ import UIKit
textButton.reset()
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 60
}
}

View File

@ -184,13 +184,13 @@ public class MoleculeStackView: ViewConstrainingView {
}
}
public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
// This will aggregate names of molecules to make an id.
guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else {
return "stack<>"
}
var name = "stack<"
for case let item as [AnyHashable: AnyHashable] in molecules {
for case let item as [AnyHashable: Any] in molecules {
if let molecule = item.optionalDictionaryForKey(KeyMolecule), let moleculeName = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) {
name.append(moleculeName + ",")
}
@ -199,7 +199,7 @@ public class MoleculeStackView: ViewConstrainingView {
return name
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
guard let items = json?.optionalArrayForKey(KeyMolecules) else {
return 0
}
@ -221,7 +221,7 @@ public class MoleculeStackView: ViewConstrainingView {
return estimatedHeight
}
public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
guard let items = json?.optionalArrayForKey(KeyMolecules) else {
return nil
}

View File

@ -28,7 +28,7 @@
dispatch_once(&onceToken, ^{
mapping = [@{
@"label": Label.class,
@"separator": SeparatorView.class,
@"line": SeparatorView.class,
@"button": ButtonView.class,
@"textButton": MFTextButton.class,
@"header": StandardHeaderView.class,
@ -39,11 +39,14 @@
@"caretButton": CaretButton.class,
@"textField" : MFTextField.class,
@"digitTextField" : MFDigitTextField.class,
@"checkbox" : MVMCoreUICheckBox.class,
@"checkbox" : Checkbox.class,
@"checkboxWithLabelView" : CheckboxWithLabelView.class,
@"cornerLabels" : CornerLabels.class,
@"progressBar": ProgressBar.class,
@"progressbar": ProgressBar.class,
@"multiProgressBar": MultiProgress.class,
@"checkbox": MVMCoreUICheckBox.class,
@"radioButton": RadioButton.class,
@"radioButtonLabel": RadioButtonLabel.class,
@"listItem": MoleculeTableViewCell.class,
@"accordionListItem": AccordionMoleculeTableViewCell.class,
@"switch": MVMCoreUISwitch.class,
@ -62,6 +65,7 @@
@"headlineBodyTextButton": HeadlineBodyTextButton.class,
@"headlineBodyTextButtonSwitch": HeadlineBodyTextButtonSwitch.class,
@"tabsListItem": TabsTableViewCell.class,
@"headlineBodyButton": HeadlineBodyButton.class,
@"eyebrowHeadlineBodyLink": EyebrowHeadlineBodyLink.class
} mutableCopy];
});

View File

@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN
@property (weak, nonatomic, nullable) MVMCoreUINavigationController *navigationController;
@property (weak, nonatomic, nullable) MFLoadingViewController *loadingViewController;
/// Tracks the current page type the user is currently viewing. KVO compliant.
@property (nonatomic, strong, nullable) NSString *currentPageType;
// for handscroll Animation on subclasses of MFScrollingViewController
@property (assign, nonatomic) BOOL enableHandScrollAnimation;

View File

@ -36,6 +36,7 @@ extern CGFloat const PaddingHorizontalLarge;
extern CGFloat const PaddingVerticalWhiteGrayView;
extern CGFloat const PaddingVerticalHeadlineAlternate;
extern CGFloat const PaddingPrimaryButtonTop;
extern CGFloat const PaddingHorizontalBetweenRelatedItems;
// These are based on the multiple of 6 rule
extern CGFloat const PaddingOne;

View File

@ -26,6 +26,7 @@ CGFloat const PaddingVerticalWhiteGrayView = 72;
CGFloat const PaddingVerticalHeadlineAlternate = 48;
CGFloat const PaddingPrimaryButtonTop = 36;
CGFloat const PaddingHorizontalBetweenRelatedItems = 16;
CGFloat const PaddingOne = 6;
CGFloat const PaddingTwo = 12;
CGFloat const PaddingThree = 18;

View File

@ -36,6 +36,13 @@
"checkbox_checked_state" = "Checked";
"checkbox_unchecked_state" = "Unchecked";
"checkbox_desc_state" = "%@ CheckBox %@";
// Radio Button
"radio_action_hint" = "Double tap to select";
"radio_selected_state" = "Selected";
"radio_not_selected_state" = "Not Selected";
"radio_desc_state" = "Option";
// Switch
"mfswitch_buttonlabel" = "Switch Button";
"AccOn" = "on";

View File

@ -37,6 +37,11 @@
"checkbox_checked_state" = "Verificado";
"checkbox_unchecked_state" = "Sin marcar";
"checkbox_desc_state" = "%@ Casilla %@";
// Radio Button
"radio_action_hint" = "Toca dos veces para seleccionar.";
"radio_selected_state" = "Seleccionado";
"radio_not_selected_state" = "No Seleccionado";
"radio_desc_state" = "Opción";
// Switch
"mfswitch_buttonlabel" = "Botón Cambiar";
"AccOn" = "encendido";

View File

@ -37,6 +37,13 @@
"checkbox_checked_state" = "Verificado";
"checkbox_unchecked_state" = "Sin marcar";
"checkbox_desc_state" = "%@ Casilla %@";
// Radio Button
"radio_action_hint" = "Toca dos veces para seleccionar.";
"radio_selected_state" = "Seleccionado";
"radio_not_selected_state" = "No Seleccionado";
"radio_desc_state" = "Opción";
// Switch
"mfswitch_buttonlabel" = "Botón Cambiar";
"AccOn" = "encendido";

View File

@ -17,4 +17,6 @@
/// Handle action
- (void)didSelectCellAtIndex:(nonnull NSIndexPath *)indexPath delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
- (void)willDisplay;
@end

View File

@ -55,9 +55,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
// MARK: - table
open override func registerWithTable() {
super.registerWithTable()
guard let moleculesInfo = moleculesInfo else {
return
}
guard let moleculesInfo = moleculesInfo else { return }
for moleculeInfo in moleculesInfo {
tableView?.register(moleculeInfo.class, forCellReuseIdentifier: moleculeInfo.identifier)
}
@ -92,6 +91,13 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
return cell
}
open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let protocolCell = cell as? MoleculeListCellProtocol {
protocolCell.willDisplay?()
}
}
open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol {
cell.didSelectCell?(atIndex: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil)
@ -124,9 +130,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
open override func addMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {
// This dispatch is needed to fix a race condition that can occur if this function is called during the table setup.
DispatchQueue.main.async {
guard let cell = sender as? MoleculeTableViewCell, let indexPath = self.tableView?.indexPath(for: cell) else {
return
}
guard let indexPath = self.tableView?.indexPath(for: sender) else { return }
var indexPaths: [IndexPath] = []
for molecule in molecules {
if let info = self.getMoleculeInfo(with: molecule) {

View File

@ -0,0 +1,21 @@
//
// CATransaction+Extension.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 10/2/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import Foundation
extension CATransaction {
/// Performs changes without activating animation actions.
static func withDisabledAnimations(_ actionBlock: ActionBlock) {
CATransaction.begin()
CATransaction.setDisableActions(true)
actionBlock()
CATransaction.commit()
}
}

View File

@ -33,6 +33,7 @@ static const CGFloat VertialShadowOffset = 6;
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
view.translatesAutoresizingMaskIntoConstraints = NO;
view.backgroundColor = [UIColor clearColor];
view.directionalLayoutMargins = NSDirectionalEdgeInsetsZero;
return view;
}
@ -198,13 +199,8 @@ static const CGFloat VertialShadowOffset = 6;
[view.rightAnchor constraintEqualToAnchor:button.rightAnchor constant:PaddingTwo].active = YES;
[view.centerYAnchor constraintEqualToAnchor:button.centerYAnchor].active = YES;
} else {
if (@available(iOS 11.0, *)) {
[button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES;
[view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES;
} else {
[NSLayoutConstraint constraintPinSubview:button pinTop:YES topConstant:PaddingOne pinBottom:NO bottomConstant:0 pinLeft:NO leftConstant:0 pinRight:YES rightConstant:PaddingTwo];
}
[button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES;
[view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES;
}
}
return button;
@ -249,16 +245,12 @@ static const CGFloat VertialShadowOffset = 6;
}
+ (nullable UIView *)getAndSetupSafeAreaViewOnView:(nonnull UIView *)view {
if (@available(iOS 11.0, *)) {
UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView];
[view addSubview:safeAreaView];
[safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES;
[view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES;
[NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
return safeAreaView;
} else {
return nil;
}
UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView];
[view addSubview:safeAreaView];
[safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES;
[view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES;
[NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
return safeAreaView;
}
#pragma mark - shadows

View File

@ -58,21 +58,13 @@
}
+ (UIEdgeInsets)getMarginsForView:(nullable UIView *)view {
if (@available(iOS 11.0, *)) {
return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing);
} else {
return view.layoutMargins;
}
return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing);
}
#pragma mark - Setters
+ (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom {
if (@available(iOS 11.0, *)) {
view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing);
} else {
view.layoutMargins = UIEdgeInsetsMake(top, leading, bottom, trailing);
}
view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing);
}
#pragma mark - Formatting
@ -152,12 +144,9 @@
CGFloat topInset = scrollview.contentInset.top;
CGFloat bottomInset = scrollview.contentInset.bottom;
// Updates for ios 11
if (@available(iOS 11.0, *)) {
if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
topInset = scrollview.adjustedContentInset.top;
bottomInset = scrollview.adjustedContentInset.bottom;
}
if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
topInset = scrollview.adjustedContentInset.top;
bottomInset = scrollview.adjustedContentInset.bottom;
}
CGFloat remainingSpace = frameHeight - contentSizeHeight - topInset - bottomInset;