merge develop
This commit is contained in:
commit
5b8fce36e6
@ -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;
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
480
MVMCoreUI/Atoms/Views/Checkbox.swift
Normal file
480
MVMCoreUI/Atoms/Views/Checkbox.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
200
MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift
Normal file
200
MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -48,9 +48,7 @@
|
||||
|
||||
- (void)setAsMolecule {
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.insetsLayoutMarginsFromSafeArea = NO;
|
||||
}
|
||||
self.insetsLayoutMarginsFromSafeArea = NO;
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
135
MVMCoreUI/Atoms/Views/RadioButton.swift
Normal file
135
MVMCoreUI/Atoms/Views/RadioButton.swift
Normal 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
|
||||
}
|
||||
}
|
||||
62
MVMCoreUI/BaseClasses/Control.swift
Normal file
62
MVMCoreUI/BaseClasses/Control.swift
Normal 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
|
||||
}
|
||||
}
|
||||
61
MVMCoreUI/BaseClasses/View.swift
Normal file
61
MVMCoreUI/BaseClasses/View.swift
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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]?
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)]];
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
135
MVMCoreUI/Molecules/HeadlineBodyButton.swift
Normal file
135
MVMCoreUI/Molecules/HeadlineBodyButton.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
314
MVMCoreUI/Molecules/Items/TableViewCell.swift
Normal file
314
MVMCoreUI/Molecules/Items/TableViewCell.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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?()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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)) {
|
||||
|
||||
90
MVMCoreUI/Molecules/RadioButtonLabel.swift
Normal file
90
MVMCoreUI/Molecules/RadioButtonLabel.swift
Normal 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
|
||||
}
|
||||
}
|
||||
54
MVMCoreUI/Molecules/RadioButtonModel.swift
Normal file
54
MVMCoreUI/Molecules/RadioButtonModel.swift
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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];
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -17,4 +17,6 @@
|
||||
/// Handle action
|
||||
- (void)didSelectCellAtIndex:(nonnull NSIndexPath *)indexPath delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
|
||||
|
||||
- (void)willDisplay;
|
||||
|
||||
@end
|
||||
|
||||
@ -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) {
|
||||
|
||||
21
MVMCoreUI/Utility/CATransaction+Extension.swift
Normal file
21
MVMCoreUI/Utility/CATransaction+Extension.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user