diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index d0075cd8..d86198eb 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -94,6 +94,7 @@ 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; 0A7ECC5D243CE85300C828E8 /* DoughnutChartItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7ECC5C243CE85300C828E8 /* DoughnutChartItemModel.swift */; }; 0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7ECC5E243CEB1200C828E8 /* ColorViewWithLabel.swift */; }; + 0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7ECC6F2441001C00C828E8 /* UIToolbar+Extension.swift */; }; 0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */; }; 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */; }; 0A7EF85F23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */; }; @@ -108,6 +109,8 @@ 0A9D09212433796500D2E6C0 /* CarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091B2433796500D2E6C0 /* CarouselIndicatorModel.swift */; }; 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */; }; 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; }; + 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */; }; + 0AB764D324460FA400E7FE72 /* UIPickerView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB764D224460FA400E7FE72 /* UIPickerView+Extension.swift */; }; 0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */; }; 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */; }; 0AE14F64238315D2005417F8 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14F63238315D2005417F8 /* TextField.swift */; }; @@ -148,6 +151,8 @@ 8D8067D32444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8067D22444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift */; }; 8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DD1E36D243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift */; }; 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DD1E36F243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift */; }; + 8DDD6C1D244D90B8006A2232 /* ListThreeColumnDataUsage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DDD6C1C244D90B8006A2232 /* ListThreeColumnDataUsage.swift */; }; + 8DDD6C1F244D90E1006A2232 /* ListThreeColumnDataUsageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DDD6C1E244D90E1006A2232 /* ListThreeColumnDataUsageModel.swift */; }; 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DEFA95B243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift */; }; 8DEFA95E243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DEFA95D243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift */; }; 942C372E241149170066E45E /* NHaasGroteskDSStd-75Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 942C372C241149170066E45E /* NHaasGroteskDSStd-75Bd.otf */; }; @@ -157,7 +162,7 @@ 9432A79F23DB47BA00719041 /* EntryFieldContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9432A79E23DB47BA00719041 /* EntryFieldContainer.swift */; }; 943784F5236B77BB006A1E82 /* Wheel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F3236B77BB006A1E82 /* Wheel.swift */; }; 943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */; }; - 943820842432382400B43AF3 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943820832432382400B43AF3 /* WebView.swift */; }; + 943820842432382400B43AF3 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943820832432382400B43AF3 /* WebView.swift */; }; 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94382085243238D100B43AF3 /* WebViewModel.swift */; }; 9445890C2385BCE300DE9FD4 /* ProgressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445890B2385BCE300DE9FD4 /* ProgressBarModel.swift */; }; 9445890E2385C3F800DE9FD4 /* MultiProgressModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445890D2385C3F800DE9FD4 /* MultiProgressModel.swift */; }; @@ -205,6 +210,8 @@ AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C10724346F4B00151545 /* RadioSwatches.swift */; }; AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C109243496DD00151545 /* RadioSwatch.swift */; }; AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */; }; + BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */; }; + BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */; }; BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */; }; BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */; }; BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */; }; @@ -536,6 +543,7 @@ 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxLabel.swift; sourceTree = ""; }; 0A7ECC5C243CE85300C828E8 /* DoughnutChartItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoughnutChartItemModel.swift; sourceTree = ""; }; 0A7ECC5E243CEB1200C828E8 /* ColorViewWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorViewWithLabel.swift; sourceTree = ""; }; + 0A7ECC6F2441001C00C828E8 /* UIToolbar+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIToolbar+Extension.swift"; sourceTree = ""; }; 0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryFieldModel.swift; sourceTree = ""; }; 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = ""; }; 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnEntryFieldModel.swift; sourceTree = ""; }; @@ -552,6 +560,8 @@ 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarouselIndicator.swift; sourceTree = ""; }; 0AA33B33239813C50067DD0F /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = ""; }; + 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Extension.swift"; sourceTree = ""; }; + 0AB764D224460FA400E7FE72 /* UIPickerView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIPickerView+Extension.swift"; sourceTree = ""; }; 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryField.swift; sourceTree = ""; }; 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryField.swift; sourceTree = ""; }; 0AE14F63238315D2005417F8 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; @@ -592,6 +602,8 @@ 8D8067D22444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePriceChangeAllTextAndLinks.swift; sourceTree = ""; }; 8DD1E36D243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalDataModel.swift; sourceTree = ""; }; 8DD1E36F243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalData.swift; sourceTree = ""; }; + 8DDD6C1C244D90B8006A2232 /* ListThreeColumnDataUsage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnDataUsage.swift; sourceTree = ""; }; + 8DDD6C1E244D90E1006A2232 /* ListThreeColumnDataUsageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnDataUsageModel.swift; sourceTree = ""; }; 8DEFA95B243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnDataUsageDividerModel.swift; sourceTree = ""; }; 8DEFA95D243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnDataUsageDivider.swift; sourceTree = ""; }; 9402C34F23A2CEA3004B974C /* LeftRightLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftRightLabelModel.swift; sourceTree = ""; }; @@ -648,6 +660,8 @@ AAB9C10724346F4B00151545 /* RadioSwatches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatches.swift; sourceTree = ""; }; AAB9C109243496DD00151545 /* RadioSwatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatch.swift; sourceTree = ""; }; AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchesModel.swift; sourceTree = ""; }; + BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMediumModel.swift; sourceTree = ""; }; + BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMedium.swift; sourceTree = ""; }; BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinksModel.swift; sourceTree = ""; }; BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinks.swift; sourceTree = ""; }; BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsectionModel.swift; sourceTree = ""; }; @@ -1091,6 +1105,8 @@ children = ( 8DD1E36D243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift */, 8DD1E36F243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift */, + 8DDD6C1E244D90E1006A2232 /* ListThreeColumnDataUsageModel.swift */, + 8DDD6C1C244D90B8006A2232 /* ListThreeColumnDataUsage.swift */, ); path = ThreeColumn; sourceTree = ""; @@ -1159,6 +1175,9 @@ D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */, D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */, 013F801823FB4A8E00AD8013 /* UIContentMode+Extension.swift */, + 0A7ECC6F2441001C00C828E8 /* UIToolbar+Extension.swift */, + 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */, + 0AB764D224460FA400E7FE72 /* UIPickerView+Extension.swift */, ); path = Extensions; sourceTree = ""; @@ -1172,6 +1191,15 @@ path = FourColumn; sourceTree = ""; }; + D20FFFB42451E32100A31DA2 /* Device */ = { + isa = PBXGroup; + children = ( + BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */, + BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */, + ); + path = Device; + sourceTree = ""; + }; D213347423842FE3008E41B3 /* Controllers */ = { isa = PBXGroup; children = ( @@ -1370,6 +1398,7 @@ D22B38EA23F4E08B00490EF6 /* List */ = { isa = PBXGroup; children = ( + D20FFFB42451E32100A31DA2 /* Device */, 52267A0523FFE0A900906CBA /* OneColumn */, D22D8396241FDE4700D3DF69 /* TwoColumn */, 8DD1E36C243B3CD900D8F2DF /* ThreeColumn */, @@ -2170,6 +2199,7 @@ D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */, D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */, AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */, + BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */, D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */, D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */, 014AA72F23C5059B006F3E93 /* ThreeLayerPageTemplateModel.swift in Sources */, @@ -2206,6 +2236,7 @@ 014AA72623C501E2006F3E93 /* ContainerModelProtocol.swift in Sources */, AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */, 011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */, + 8DDD6C1D244D90B8006A2232 /* ListThreeColumnDataUsage.swift in Sources */, AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */, D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, 944589232385DA9600DE9FD4 /* ImageViewModel.swift in Sources */, @@ -2214,6 +2245,7 @@ 525019DE2406430800EED91C /* ListProgressBarData.swift in Sources */, D28A837F23CCA96400DFE4FC /* TabsModel.swift in Sources */, 012A88EC238F084D00FE3DA1 /* FooterModel.swift in Sources */, + 8DDD6C1F244D90E1006A2232 /* ListThreeColumnDataUsageModel.swift in Sources */, BB55B51F244482D2002001AD /* ListRightVariablePriceChangeBodyTextModel.swift in Sources */, D2A514672213885800345BFB /* MoleculeHeaderView.swift in Sources */, D29E28D823D21AB800ACEA85 /* StringAndMoleculeView.swift in Sources */, @@ -2248,6 +2280,7 @@ AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */, D2B18B812360945C00A9AEDC /* View.swift in Sources */, C6FA7D5423C77A4A00A3614A /* NumberedList.swift in Sources */, + 0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, 525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */, 0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */, @@ -2354,6 +2387,7 @@ 012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */, + 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */, C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */, @@ -2419,6 +2453,7 @@ D21B7F73243BAC6800051ABF /* CollectionItemModelProtocol.swift in Sources */, C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, + BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */, D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */, 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */, 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */, @@ -2435,6 +2470,7 @@ 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */, D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, + 0AB764D324460FA400E7FE72 /* UIPickerView+Extension.swift in Sources */, D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */, 94C661D923CCF4B400D9FE5B /* LeftRightLabelModel.swift in Sources */, 011D95AB2405C553000E3791 /* FormItemProtocol.swift in Sources */, diff --git a/MVMCoreUI/Actions/ActionCollapseNotificationModel.swift b/MVMCoreUI/Actions/ActionCollapseNotificationModel.swift index 1a665b80..581c8687 100644 --- a/MVMCoreUI/Actions/ActionCollapseNotificationModel.swift +++ b/MVMCoreUI/Actions/ActionCollapseNotificationModel.swift @@ -9,6 +9,7 @@ import UIKit @objcMembers public class ActionCollapseNotificationModel: ActionModelProtocol { + public static var identifier: String = "collapseNotification" public var actionType: String public var extraParameters: JSONValueDictionary? diff --git a/MVMCoreUI/Actions/ActionOpenPanelModel.swift b/MVMCoreUI/Actions/ActionOpenPanelModel.swift index 6ada6f04..9149b32b 100644 --- a/MVMCoreUI/Actions/ActionOpenPanelModel.swift +++ b/MVMCoreUI/Actions/ActionOpenPanelModel.swift @@ -9,7 +9,7 @@ import Foundation public class ActionOpenPanelModel: ActionModelProtocol { - + public enum Panel: String, Codable { case left case right diff --git a/MVMCoreUI/Actions/ActionTopAlertModel.swift b/MVMCoreUI/Actions/ActionTopAlertModel.swift index 29838c76..82d9fa17 100644 --- a/MVMCoreUI/Actions/ActionTopAlertModel.swift +++ b/MVMCoreUI/Actions/ActionTopAlertModel.swift @@ -9,6 +9,7 @@ import Foundation @objcMembers public class ActionTopAlertModel: ActionModelProtocol { + public static var identifier: String = "topAlert" public var actionType: String = ActionTopAlertModel.identifier public var pageType: String diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryField.swift index f7083df5..ee697a3e 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryField.swift @@ -69,7 +69,7 @@ import UIKit textFieldTrailingConstraint = dropDownCaretView.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 6) textFieldTrailingConstraint?.isActive = true - container.trailingAnchor.constraint(equalTo: dropDownCaretView.trailingAnchor, constant: 16).isActive = true + container.trailingAnchor.constraint(equalTo: dropDownCaretView.trailingAnchor, constant: Padding.Four).isActive = true dropDownCaretView.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryField.swift index a7a483fc..9860cd5b 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryField.swift @@ -31,10 +31,9 @@ import UIKit return formatter }() + /// Update the property value to alter the format of how the date is presented. public var dateFormat: String = "MMM d, y" { - didSet { - dateFormatter.dateFormat = dateFormat - } + didSet { dateFormatter.dateFormat = dateFormat } } //-------------------------------------------------- @@ -69,10 +68,10 @@ import UIKit public override func setupFieldContainerContent(_ container: UIView) { super.setupFieldContainerContent(container) - datePicker = MVMCoreUICommonViewsUtility.addDatePicker(to: textField) + datePicker = UIDatePicker.addDatePicker(to: textField) datePicker?.addTarget(self, action: #selector(pickerValueChanged), for: .valueChanged) datePicker?.timeZone = NSTimeZone.system - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: self) + UIToolbar.addDismissToolbar(to: textField, delegate: self, action: #selector(dismissFieldInput)) } @objc public func setDatePickerDuration(from startDate: Date?, to endDate: Date?, showStartDate: Bool = true) { @@ -104,7 +103,7 @@ import UIKit } } - @objc override func dismissFieldInput(_ sender: Any?) { + @objc public override func dismissFieldInput(_ sender: Any?) { setTextWith(date: datePicker?.date) super.dismissFieldInput(sender) diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift index cdafc9e9..86c072dd 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift @@ -22,24 +22,25 @@ //-------------------------------------------------- private enum CodingKeys: String, CodingKey { - case moleculeName case dateFormat } //-------------------------------------------------- - // MARK: - Initializers + // MARK: - Codec //-------------------------------------------------- required public init(from decoder: Decoder) throws { try super.init(from: decoder) let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - dateFormat = try typeContainer.decodeIfPresent(String.self, forKey: .dateFormat) ?? "MMM d, y" + + if let dateFormat = try typeContainer.decodeIfPresent(String.self, forKey: .dateFormat) { + self.dateFormat = dateFormat + } } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) try container.encode(dateFormat, forKey: .dateFormat) } } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift index 38adee03..8ae4365f 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift @@ -211,7 +211,9 @@ import UIKit let digitBox = DigitBox() digitBox.isAccessibilityElement = true - digitBox.digitField.inputAccessoryView = MVMCoreUICommonViewsUtility.getToolbarWithDoneButton(delegate: self) + let observingDelegate = delegateObject?.observingTextFieldDelegate ?? self + digitBox.digitField.inputAccessoryView = UIToolbar.getToolbarWithDoneButton(delegate: observingDelegate, + action: #selector(observingDelegate.dismissFieldInput)) digitBox.digitField.delegate = self digitBox.digitBoxDelegate = self return digitBox @@ -246,15 +248,6 @@ import UIKit } } - @objc public override func defaultValidationBlock() { - - validationBlock = { enteredValue in - guard let enteredValue = enteredValue else { return false } - - return enteredValue.count > 0 && enteredValue.count == self.digitBoxes.count - } - } - @objc public func selectPreviousDigitField(_ currentTextField: UITextField?, clear: Bool) { var selectPreviousField = false @@ -322,7 +315,7 @@ import UIKit return true } - @objc override func dismissFieldInput(_ sender: Any?) { + @objc public override func dismissFieldInput(_ sender: Any?) { digitBoxes.forEach { if $0.isSelected { @@ -337,12 +330,18 @@ import UIKit guard let model = model as? DigitEntryFieldModel else { return } numberOfDigits = model.digits - setAsSecureTextEntry(model.secureEntry) - for digitBox in digitBoxes { - digitBox.digitField.inputAccessoryView = MVMCoreUICommonViewsUtility.getToolbarWithDoneButton(delegate: delegateObject?.observingTextFieldDelegate ?? self) + if let entryType = model.type { + setAsSecureTextEntry(entryType == .secure || entryType == .password) } + let observingDelegate = delegateObject?.observingTextFieldDelegate ?? self + + digitBoxes.forEach { + $0.digitField.inputAccessoryView = UIToolbar.getToolbarWithDoneButton(delegate: observingDelegate, + action: #selector(observingDelegate.dismissFieldInput)) + } + super.set(with: model, delegateObject, additionalData) } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift index 0291066c..b906244d 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift @@ -17,34 +17,31 @@ } public var digits: Int = 4 - public var secureEntry: Bool = false //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- private enum CodingKeys: String, CodingKey { - case moleculeName case digits - case secureEntry } //-------------------------------------------------- - // MARK: - Initializers + // MARK: - Codec //-------------------------------------------------- required public init(from decoder: Decoder) throws { try super.init(from: decoder) let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - digits = try typeContainer.decodeIfPresent(Int.self, forKey: .digits) ?? 4 - secureEntry = try typeContainer.decodeIfPresent(Bool.self, forKey: .secureEntry) ?? false + + if let digits = try typeContainer.decodeIfPresent(Int.self, forKey: .digits) { + self.digits = digits + } } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) try container.encode(digits, forKey: .digits) - try container.encode(secureEntry, forKey: .secureEntry) } } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift index 7034c3e3..6bb82fe1 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift @@ -20,7 +20,7 @@ import UIKit public private(set) var titleLabel: Label = { let label = Label() - label.font = MFStyler.fontRegularMicro() + label.font = Styler.Font.RegularMicro.getFont() label.textColor = .mvmBlack label.setContentCompressionResistancePriority(.required, for: .vertical) return label @@ -31,12 +31,12 @@ import UIKit /// Provides contextual information on the TextField. public private(set) var feedbackLabel: Label = { let label = Label() - label.font = MFStyler.fontRegularMicro() + label.font = Styler.Font.RegularMicro.getFont() label.textColor = .mvmBlack label.setContentCompressionResistancePriority(.required, for: .vertical) return label }() - + //-------------------------------------------------- // MARK: - Delegate //-------------------------------------------------- @@ -48,14 +48,6 @@ import UIKit //-------------------------------------------------- public var isValid: Bool = false - public var errorMessage: String? - public var standardMessage: String? { - didSet { - if !showError { - feedback = standardMessage - } - } - } //-------------------------------------------------- // MARK: - Computed Properties @@ -75,7 +67,7 @@ import UIKit public var showError: Bool { get { return entryFieldContainer.showError } set (error) { - self.feedback = error ? self.errorMessage : self.standardMessage + self.feedback = error ? entryFieldModel?.errorMessage : entryFieldModel?.feedback self.entryFieldContainer.showError = error } } @@ -121,6 +113,10 @@ import UIKit } } + public var entryFieldModel: EntryFieldModel? { + return model as? EntryFieldModel + } + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- @@ -174,8 +170,6 @@ import UIKit @objc final public override func setupView() { super.setupView() - guard subviews.isEmpty else { return } - isAccessibilityElement = false setContentCompressionResistancePriority(.required, for: .vertical) accessibilityElements = [titleLabel, feedbackLabel] @@ -193,7 +187,7 @@ import UIKit entryFieldContainer.setContentCompressionResistancePriority(.required, for: .vertical) setupFieldContainerContent(entryFieldContainer) - titleContainerDistance = entryFieldContainer.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4) + titleContainerDistance = entryFieldContainer.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: Padding.One) titleContainerDistance?.isActive = true entryFieldContainerLeading = entryFieldContainer.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) entryFieldContainerLeading?.isActive = true @@ -202,7 +196,7 @@ import UIKit addSubview(feedbackLabel) - feedbackContainerDistance = feedbackLabel.topAnchor.constraint(equalTo: entryFieldContainer.bottomAnchor, constant: PaddingOne) + feedbackContainerDistance = feedbackLabel.topAnchor.constraint(equalTo: entryFieldContainer.bottomAnchor, constant: Padding.Two) feedbackContainerDistance?.isActive = true feedbackLabelLeading = feedbackLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) feedbackLabelLeading?.isActive = true @@ -217,11 +211,11 @@ import UIKit entryFieldContainer.refreshUI() } - /// Method to override. - /// Intended to add the interactive content (i.e. textField) to the entryFieldContainer. - @objc open func setupFieldContainerContent(_ container: UIView) { - // To be overridden by subclass. - } + /** + Method to override. + Intended to add the interactive content (i.e. textField) to the entryFieldContainer. + */ + @objc open func setupFieldContainerContent(_ container: UIView) { } @objc open override func updateView(_ size: CGFloat) { super.updateView(size) @@ -240,10 +234,11 @@ import UIKit backgroundColor = .clear isAccessibilityElement = false - titleLabel.font = MFStyler.fontRegularMicro() + titleLabel.font = Styler.Font.RegularMicro.getFont() titleLabel.textColor = .mvmBlack - feedbackLabel.font = MFStyler.fontRegularMicro() + feedbackLabel.font = Styler.Font.RegularMicro.getFont() feedbackLabel.textColor = .mvmBlack + feedbackLabel.text = nil entryFieldContainer.reset() } @@ -257,7 +252,6 @@ import UIKit title = model.title feedback = model.feedback - errorMessage = model.errorMessage isEnabled = model.enabled if let isLocked = model.locked { diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift index 79de359b..337d847d 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift @@ -22,29 +22,22 @@ import Foundation public var backgroundColor: Color? public var title: String? public var feedback: String? - public var errorMessage: String = "" + public var errorMessage: String? public var enabled: Bool = true public var locked: Bool? public var selected: Bool? public var text: String? - public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? - + public var isValid: Bool? { - didSet { - updateUI?() - } + didSet { updateUI?() } } /// Temporary binding mechanism for the view to update on enable changes. - public var updateUI: (() -> Void)? + public var updateUI: (() -> ())? - public func setValidity(_ valid: Bool, rule: RulesProtocol) { - self.isValid = valid - } - //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- @@ -62,11 +55,19 @@ import Foundation case fieldKey case groupName } - + + //-------------------------------------------------- + // MARK: - Validation Methods + //-------------------------------------------------- + public func formFieldValue() -> AnyHashable? { return text } + public func setValidity(_ valid: Bool, rule: RulesProtocol) { + self.isValid = valid + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -76,17 +77,20 @@ import Foundation baseValue = text } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) title = try typeContainer.decodeIfPresent(String.self, forKey: .title) feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback) - errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage) ?? "" + errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage) enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true locked = try typeContainer.decodeIfPresent(Bool.self, forKey: .locked) selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) text = try typeContainer.decodeIfPresent(String.self, forKey: .text) - baseValue = text fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { @@ -100,11 +104,11 @@ import Foundation try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(title, forKey: .title) try container.encodeIfPresent(feedback, forKey: .feedback) - try container.encode(errorMessage, forKey: .errorMessage) - try container.encode(enabled, forKey: .enabled) - try container.encode(locked, forKey: .locked) - try container.encode(selected, forKey: .selected) try container.encodeIfPresent(text, forKey: .text) + try container.encodeIfPresent(locked, forKey: .locked) + try container.encodeIfPresent(selected, forKey: .selected) + try container.encodeIfPresent(errorMessage, forKey: .errorMessage) + try container.encode(enabled, forKey: .enabled) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(groupName, forKey: .groupName) } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryField.swift index bd6d793c..cc8a4c71 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryField.swift @@ -8,6 +8,8 @@ import UIKit +public typealias TextFieldAndPickerDelegate = (UITextFieldDelegate & UIPickerViewDelegate & UIPickerViewDataSource) + open class ItemDropdownEntryField: BaseDropdownEntryField { //-------------------------------------------------- @@ -62,7 +64,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { @objc open override func setupFieldContainerContent(_ container: UIView) { super.setupFieldContainerContent(container) - pickerView = MVMCoreUICommonViewsUtility.addPicker(to: textField, delegate: self) + pickerView = UIPickerView.addPicker(to: textField, delegate: self, dismissAction: #selector(dismissFieldInput)) textField.hideBlinkingCaret = true textField.autocorrectionType = .no uiTextFieldDelegate = self diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift index 56612b7b..2105120c 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift @@ -17,17 +17,21 @@ public var options: [String] = [] public var selectedIndex: Int = 0 + + public override func formFieldValue() -> AnyHashable? { + guard !options.isEmpty else { return nil } + return options[selectedIndex] + } //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- private enum CodingKeys: String, CodingKey { - case moleculeName case options case selectedIndex } - + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -35,14 +39,17 @@ required public init(from decoder: Decoder) throws { try super.init(from: decoder) let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + options = try typeContainer.decode([String].self, forKey: .options) - selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) ?? 0 + + if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) { + self.selectedIndex = selectedIndex + } } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) try container.encode(options, forKey: .options) try container.encode(options, forKey: .selectedIndex) } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryField.swift index 77b5a4a4..d80a008b 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryField.swift @@ -48,6 +48,18 @@ import MVMCore set { text = MVMCoreUIUtility.formatMdn(newValue) } } + /// Toggles selected or original (unselected) UI. + public override var isSelected: Bool { + get { return entryFieldContainer.isSelected } + set (selected) { + if selected && showError { + showError = false + } + + super.isSelected = selected + } + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -77,11 +89,14 @@ import MVMCore super.setupFieldContainerContent(container) textField.keyboardType = .numberPad + } + + open override func setupTextFieldToolbar() { - let toolbar = MVMCoreUICommonViewsUtility.makeEmptyToolbar() + let toolbar = UIToolbar.createEmptyToolbar() let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) - let contacts = UIBarButtonItem(title: MVMCoreUIUtility.hardcodedString(withKey: "textfield_contacts_barbutton"), style: .plain, target: self, action: #selector(getContacts(_:))) - let dismissButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissFieldInput(_:))) + let contacts = UIBarButtonItem(title: MVMCoreUIUtility.hardcodedString(withKey: "textfield_contacts_barbutton"), style: .plain, target: self, action: #selector(getContacts)) + let dismissButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissFieldInput)) toolbar.items = [contacts, space, dismissButton] textField.inputAccessoryView = toolbar } @@ -107,18 +122,18 @@ import MVMCore isValid = true return true } - - let isValid = hasValidMDN() - - if isValid { + + isValid = hasValidMDN() + + if self.isValid { showError = false - + } else { - errorMessage = errorMessage ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message") + entryFieldModel?.errorMessage = entryFieldModel?.errorMessage ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message") showError = true UIAccessibility.post(notification: .layoutChanged, argument: textField) } - + return isValid } @@ -196,7 +211,7 @@ import MVMCore } @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { - + return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift index e0055d2a..f95c918d 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift @@ -15,7 +15,7 @@ import UIKit /// Called when the entered text becomes invalid based on the validation block @objc optional func isInvalid(textfield: TextEntryField?) /// Dismisses the keyboard. - @objc optional func dismissFieldInput(sender: Any?) + @objc optional func dismissFieldInput(_ sender: Any?) } @@ -28,7 +28,7 @@ import UIKit let textField = TextField() textField.isAccessibilityElement = true textField.setContentCompressionResistancePriority(.required, for: .vertical) - textField.font = MFStyler.fontRegularBodyLarge() + textField.font = Styler.Font.RegularBodyLarge.getFont() textField.textColor = .mvmBlack textField.smartQuotesType = .no textField.smartDashesType = .no @@ -36,13 +36,19 @@ import UIKit return textField }() + public lazy var errorImage: UIImageView = { + let image = MVMCoreUIUtility.imageNamed("alert_standard") + let imageView = UIImageView(image: image) + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.heightAnchor.constraint(equalToConstant: 20).isActive = true + imageView.widthAnchor.constraint(equalToConstant: 20).isActive = true + return imageView + }() + //-------------------------------------------------- // MARK: - Stored Properties //-------------------------------------------------- - /// Set enabled and disabled colors to be utilized when setting this texfield's isEnabled property. - public var textColor: (enabled: UIColor?, disabled: UIColor?) = (.mvmBlack, .mvmCoolGray3) - private var observingForChange: Bool = false /// Validate on each entry in the textField. Default: true @@ -54,7 +60,7 @@ import UIKit public var textEntryFieldModel: TextEntryFieldModel? { return model as? TextEntryFieldModel } - + //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- @@ -68,7 +74,7 @@ import UIKit guard let self = self else { return } self.textField.isEnabled = enabled - self.textField.textColor = enabled ? self.textColor.enabled : self.textColor.disabled + self.textField.textColor = enabled ? self.textEntryFieldModel?.enabledTextColor.uiColor : self.textEntryFieldModel?.disabledTextColor.uiColor } } } @@ -78,11 +84,15 @@ import UIKit set (error) { if error { - textField.accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message") ?? "", textField.text ?? "", errorMessage ?? "") + textField.accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message") ?? "", textField.text ?? "", entryFieldModel?.errorMessage ?? "") } else { textField.accessibilityValue = nil } + if textField.isSecureTextEntry { + showErrorView(error) + } + super.showError = error } } @@ -102,12 +112,6 @@ import UIKit set { textField.placeholder = newValue } } - //-------------------------------------------------- - // MARK: - Property Observers - //-------------------------------------------------- - - public var validationBlock: ((_ value: String?) -> Bool)? - //-------------------------------------------------- // MARK: - Delegate Properties //-------------------------------------------------- @@ -169,21 +173,21 @@ import UIKit @objc open override func setupFieldContainerContent(_ container: UIView) { - textField.font = MFStyler.fontRegularBodyLarge() + textField.font = Styler.Font.RegularBodyLarge.getFont() container.addSubview(textField) NSLayoutConstraint.activate([ - textField.heightAnchor.constraint(equalToConstant: 24), - textField.topAnchor.constraint(equalTo: container.topAnchor, constant: 12), - textField.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16), - container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 12) + textField.heightAnchor.constraint(equalToConstant: Padding.Five), + textField.topAnchor.constraint(equalTo: container.topAnchor, constant: Padding.Three), + textField.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: Padding.Three), + container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: Padding.Three) ]) - textFieldTrailingConstraint = container.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 16) + textFieldTrailingConstraint = container.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: Padding.Three) textFieldTrailingConstraint?.isActive = true textField.addTarget(self, action: #selector(startEditing), for: .editingDidBegin) - textField.addTarget(self, action: #selector(dismissFieldInput(_:)), for: .editingDidEnd) + textField.addTarget(self, action: #selector(dismissFieldInput), for: .editingDidEnd) let tap = UITapGestureRecognizer(target: self, action: #selector(startEditing)) entryFieldContainer.addGestureRecognizer(tap) @@ -194,37 +198,31 @@ import UIKit @objc open override func updateView(_ size: CGFloat) { super.updateView(size) - textField.font = MFStyler.fontRegularBodyLarge() + textField.font = Styler.Font.RegularBodyLarge.getFont() layoutIfNeeded() } open override func reset() { super.reset() - textField.font = MFStyler.fontRegularBodyLarge() + textField.font = Styler.Font.RegularBodyLarge.getFont() } - @objc deinit { - setBothTextDelegates(to: nil) - } - @objc public func setBothTextDelegates(to delegate: (UITextFieldDelegate & ObservingTextFieldDelegate)?) { observingTextFieldDelegate = delegate uiTextFieldDelegate = delegate } + open func setupTextFieldToolbar() { + let observingDelegate = observingTextFieldDelegate ?? self + textField.inputAccessoryView = UIToolbar.getToolbarWithDoneButton(delegate: observingDelegate, + action: #selector(observingDelegate.dismissFieldInput)) + } + //-------------------------------------------------- // MARK: - Observing for Change (TextFieldDelegate) //-------------------------------------------------- - public func defaultValidationBlock() { - - validationBlock = { enteredValue in - guard let enteredValue = enteredValue else { return false } - return enteredValue.count > 0 - } - } - @discardableResult @objc override open func resignFirstResponder() -> Bool { if validateWhenDoneEditing { @@ -240,11 +238,11 @@ import UIKit text = textField.text _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) } - + @objc public func updateValidation(_ isValid: Bool) { let previousValidity = self.isValid self.isValid = isValid - + if previousValidity && !isValid { showError = true observingTextFieldDelegate?.isInvalid?(textfield: self) @@ -276,10 +274,30 @@ import UIKit } } - @objc func dismissFieldInput(_ sender: Any?) { + @objc public func dismissFieldInput(_ sender: Any?) { resignFirstResponder() } + private func showErrorView(_ show: Bool) { + + if show { + entryFieldContainer.addSubview(errorImage) + + textFieldTrailingConstraint?.isActive = false + textFieldTrailingConstraint = errorImage.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: Padding.Two) + textFieldTrailingConstraint?.isActive = true + + entryFieldContainer.trailingAnchor.constraint(equalTo: errorImage.trailingAnchor, constant: Padding.Three).isActive = true + errorImage.centerYAnchor.constraint(equalTo: entryFieldContainer.centerYAnchor).isActive = true + + } else { + errorImage.removeFromSuperview() + textFieldTrailingConstraint?.isActive = false + textFieldTrailingConstraint = entryFieldContainer.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: Padding.Two) + textFieldTrailingConstraint?.isActive = true + } + } + //-------------------------------------------------- // MARK: - MoleculeViewProtocol //-------------------------------------------------- @@ -288,37 +306,39 @@ import UIKit super.set(with: model, delegateObject, additionalData) guard let model = model as? TextEntryFieldModel else { return } - + model.updateUI = { [weak self] in MVMCoreDispatchUtility.performBlock(onMainThread: { - if self?.isSelected ?? false { - self?.updateValidation(model.isValid ?? true) + guard let self = self else { return } + + if self.isSelected { + self.updateValidation(model.isValid ?? true) } }) } + self.delegateObject = delegateObject - FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) - textColor.enabled = model.enabledTextColor?.uiColor - textColor.disabled = model.disabledTextColor?.uiColor + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) text = model.text placeholder = model.placeholder switch model.type { - case .password: + case .password, .secure: textField.isSecureTextEntry = true + case .number: textField.keyboardType = .numberPad + case .email: textField.keyboardType = .emailAddress + default: break } - defaultValidationBlock() - uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate observingTextFieldDelegate = delegateObject?.observingTextFieldDelegate - textField.inputAccessoryView = MVMCoreUICommonViewsUtility.getToolbarWithDoneButton(delegate: observingTextFieldDelegate ?? self) + setupTextFieldToolbar() } } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift index 0057b46a..491e9891 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift @@ -8,9 +8,13 @@ @objcMembers public class TextEntryFieldModel: EntryFieldModel { + //-------------------------------------------------- + // MARK: - Types + //-------------------------------------------------- public enum EntryType: String, Codable { case password + case secure case number case email } @@ -24,8 +28,8 @@ } public var placeholder: String? - public var enabledTextColor: Color? - public var disabledTextColor: Color? + public var enabledTextColor: Color = Color(uiColor: .mvmBlack) + public var disabledTextColor: Color = Color(uiColor: .mvmCoolGray3) public var type: EntryType? //-------------------------------------------------- @@ -34,7 +38,6 @@ private enum CodingKeys: String, CodingKey { case moleculeName - case text case placeholder case enabledTextColor case disabledTextColor @@ -42,27 +45,31 @@ } //-------------------------------------------------- - // MARK: - Initializers + // MARK: - Codec //-------------------------------------------------- required public init(from decoder: Decoder) throws { try super.init(from: decoder) let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - text = try typeContainer.decodeIfPresent(String.self, forKey: .text) placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder) - enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor) - disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) type = try typeContainer.decodeIfPresent(EntryType.self, forKey: .type) + + if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor) { + self.enabledTextColor = enabledTextColor + } + + if let disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) { + self.disabledTextColor = disabledTextColor + } } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(text, forKey: .text) try container.encodeIfPresent(placeholder, forKey: .placeholder) - try container.encodeIfPresent(enabledTextColor, forKey: .enabledTextColor) - try container.encodeIfPresent(disabledTextColor, forKey: .disabledTextColor) + try container.encode(enabledTextColor, forKey: .enabledTextColor) + try container.encode(disabledTextColor, forKey: .disabledTextColor) try container.encodeIfPresent(type, forKey: .type) } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift index 75e9675a..14cfb7b7 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift @@ -131,8 +131,26 @@ public typealias ActionBlockConfirmation = () -> (Bool) private var widthConstraint: NSLayoutConstraint? private func constrainKnob() { - knobLeadingConstraint?.isActive = !isOn - knobTrailingConstraint?.isActive = isOn + + knobLeadingConstraint?.isActive = false + knobTrailingConstraint?.isActive = false + + _ = isOn ? constrainKnobOn() : constrainKnobOff() + + knobTrailingConstraint?.isActive = true + knobLeadingConstraint?.isActive = true + } + + private func constrainKnobOn() { + + knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 1) + knobLeadingConstraint = knobView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor) + } + + private func constrainKnobOff() { + + knobTrailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: knobView.trailingAnchor) + knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) } //-------------------------------------------------- @@ -189,6 +207,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) layer.cornerRadius = Self.getContainerHeight() / 2.0 knobView.layer.cornerRadius = Self.getKnobHeight() / 2.0 + + changeStateNoAnimation(isOn) } public override func setupView() { @@ -218,9 +238,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) knobView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true - knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 1) - knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) - knobLeadingConstraint?.isActive = true + constrainKnobOff() } public override func reset() { @@ -228,9 +246,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) backgroundColor = containerTintColor.off knobView.backgroundColor = knobTintColor.off + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") isAnimated = true - isOn = false - constrainKnob() didToggleAction = nil shouldToggleAction = { return true } } @@ -352,10 +369,15 @@ public typealias ActionBlockConfirmation = () -> (Bool) containerTintColor.off = model.offTintColor.uiColor knobTintColor.on = model.onKnobTintColor.uiColor knobTintColor.off = model.offKnobTintColor.uiColor - changeStateNoAnimation(model.state) + isOn = model.state + changeStateNoAnimation(isOn) isAnimated = model.animated isEnabled = model.enabled + if let accessibileString = model.accessibilityText { + accessibilityLabel = accessibileString + } + if let actionMap = model.action?.toJSON() { didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atomic/Extensions/UIDatePicker+Extension.swift b/MVMCoreUI/Atomic/Extensions/UIDatePicker+Extension.swift new file mode 100644 index 00000000..4c1acfcb --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/UIDatePicker+Extension.swift @@ -0,0 +1,37 @@ +// +// UIDatePicker+Extension.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/14/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +public extension UIDatePicker { + + class func addDatePicker(to textField: UITextField) -> UIDatePicker { + + let datePicker = UIDatePicker() + datePicker.backgroundColor = .mvmWhite + datePicker.datePickerMode = .date + + let locale = NSLocale.current as NSLocale + datePicker.locale = locale as Locale + datePicker.calendar = locale.object(forKey: .calendar) as? Calendar + textField.inputView = datePicker + + return datePicker + } + + class func addTimeAndDatePicker(to textField: UITextField) -> UIDatePicker { + + let datePicker = UIDatePicker() + datePicker.backgroundColor = .mvmWhite + datePicker.datePickerMode = .time + textField.inputView = datePicker + + return datePicker + } +} diff --git a/MVMCoreUI/Atomic/Extensions/UIPickerView+Extension.swift b/MVMCoreUI/Atomic/Extensions/UIPickerView+Extension.swift new file mode 100644 index 00000000..aa933e05 --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/UIPickerView+Extension.swift @@ -0,0 +1,38 @@ +// +// UIPickerView.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/14/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +public extension UIPickerView { + + class func createPickerView() -> UIPickerView { + + let picker = UIPickerView(frame: .zero) + picker.backgroundColor = .mvmWhite + picker.showsSelectionIndicator = true + return picker + } + + class func addPicker(to textField: UITextField, delegate: TextFieldAndPickerDelegate?, dismissAction: Selector?) -> UIPickerView { + + // Sets up the picker (same tag as the textfield) + let picker = createPickerView() + picker.delegate = delegate + picker.dataSource = delegate + picker.tag = textField.tag + textField.inputView = picker + + // Adds a dismiss toolbar, since all fields with pickers should have one. + if let dismissAction = dismissAction { + UIToolbar.addDismissToolbar(to: textField, delegate: delegate, action: dismissAction) + } + + return picker + } +} diff --git a/MVMCoreUI/Atomic/Extensions/UIToolbar+Extension.swift b/MVMCoreUI/Atomic/Extensions/UIToolbar+Extension.swift new file mode 100644 index 00000000..cad763a6 --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/UIToolbar+Extension.swift @@ -0,0 +1,56 @@ +// +// UIToolbar+Extension.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/10/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol TextFieldOrView { } + +extension UITextView: TextFieldOrView { } +extension UITextField: TextFieldOrView { } + + +public extension UIToolbar { + + class func createEmptyToolbar() -> UIToolbar { + + let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44)) + toolbar.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleWidth] + toolbar.barStyle = .default + toolbar.barTintColor = .mvmCoolGray3 + toolbar.tintColor = .mvmBlack + toolbar.isTranslucent = true + + return toolbar + } + + class func getToolbarWithDoneButton(delegate: Any?, action: Selector) -> UIToolbar { + + let toolbar = createEmptyToolbar() + let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let button = UIBarButtonItem(barButtonSystemItem: .done, target: delegate, action: action) + toolbar.setItems([space, button], animated: false) + + return toolbar + } + + class func addDismissToolbar(to object: TextFieldOrView, delegate: Any?, action: Selector) { + + let toolbar = Self.getToolbarWithDoneButton(delegate: delegate, action: action) + + switch object { + case is UITextField: + (object as? UITextField)?.inputAccessoryView = toolbar + + case is UITextView: + (object as? UITextView)?.inputAccessoryView = toolbar + + default: + return + } + } +} diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index f64d5031..e3b98256 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -152,6 +152,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListTwoColumnPriceDetails.self, viewModelClass: ListTwoColumnPriceDetailsModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListTwoColumnPriceDescription.self, viewModelClass: ListTwoColumnPriceDescriptionModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnInternationalData.self, viewModelClass: ListThreeColumnInternationalDataModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnDataUsage.self, viewModelClass: ListThreeColumnDataUsageModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListFourColumnDataUsageListItem.self, viewModelClass: ListFourColumnDataUsageListItemModel.self) // Designed Section Dividers @@ -168,6 +169,10 @@ import Foundation // Designed Headers MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2NoButtonsBodyText.self, viewModelClass: HeadersH2NoButtonsBodyTextModel.self) + + + // Device Items + MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexButtonMedium.self, viewModelClass: ListDeviceComplexButtonMediumModel.self) // TODO: Need View try? ModelRegistry.register(TabsModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift new file mode 100644 index 00000000..f8ab918d --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift @@ -0,0 +1,79 @@ +// +// ListDeviceComplexButtonMedium.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 21/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListDeviceComplexButtonMedium: TableViewCell { + + public var verticalStack: Stack + public let eyebrow = Label.createLabelRegularMicro(true) + public let headline = Label.createLabelBoldTitleMedium(true) + public let body = Label.createLabelRegularBodySmall(true) + public let body2 = Label.createLabelRegularBodySmall(true) + public let button = PillButton(frame: .zero) + public let rightImageView = MFLoadImageView() + public var stack: Stack + + // MARK: - Initializers + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + rightImageView.addSizeConstraintsForAspectRatio = true + rightImageView.heightAnchor.constraint(equalToConstant: 116.0).isActive = true + rightImageView.widthAnchor.constraint(equalToConstant: 116.0).isActive = true + verticalStack = Stack.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), + (view: headline, model: StackItemModel(horizontalAlignment: .leading)), + (view: body, model: StackItemModel(horizontalAlignment: .leading)), + (view: body2, model: StackItemModel(horizontalAlignment: .leading)), + (view: button, model: StackItemModel(spacing:16, horizontalAlignment: .leading))], + axis: .vertical, spacing: 0) + stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .leading)), + (view: rightImageView, model: StackItemModel(horizontalAlignment: .fill, verticalAlignment: .center))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - MFViewProtocol + open override func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + verticalStack.restack() + } + + // MARK: - ModelMoleculeViewProtocol + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListDeviceComplexButtonMediumModel else { return } + verticalStack.updateContainedMolecules(with: [model.eyebrow, + model.headline, + model.body, + model.body2, + model.button], + delegateObject, additionalData) + rightImageView.set(with: model.image, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 120 + } + + public func setDefault() { + eyebrow.styleRegularMicro(true) + headline.styleBoldTitleMedium(true) + body.styleRegularBodySmall(true) + body2.styleRegularBodySmall(true) + eyebrow.textColor = .mvmCoolGray6 + } + + open override func reset() { + super.reset() + setDefault() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMediumModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMediumModel.swift new file mode 100644 index 00000000..7f676795 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMediumModel.swift @@ -0,0 +1,68 @@ +// +// ListDeviceComplexButtonMediumModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 21/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +public class ListDeviceComplexButtonMediumModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "listDvcBtnM" + public var eyebrow: LabelModel? + public var headline: LabelModel? + public var body: LabelModel? + public var body2: LabelModel? + public var button: ButtonModel + public var image: ImageViewModel + + public init(eyebrow: LabelModel, headline:LabelModel, body: LabelModel, body2: LabelModel, button: ButtonModel, image: ImageViewModel) { + self.eyebrow = eyebrow + self.headline = headline + self.body = body + self.body2 = body2 + self.button = button + self.image = image + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + button.size = .tiny + button.style = .secondary + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case eyebrow + case headline + case body + case body2 + case button + case image + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow) + headline = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .headline) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) + body2 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body2) + button = try typeContainer.decode(ButtonModel.self, forKey: .button) + image = try typeContainer.decode(ImageViewModel.self, forKey: .image) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(eyebrow, forKey: .eyebrow) + try container.encodeIfPresent(headline, forKey: .headline) + try container.encodeIfPresent(body, forKey: .body) + try container.encodeIfPresent(body2, forKey: .body2) + try container.encode(button, forKey: .button) + try container.encode(image, forKey: .image) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift index 1ea1a4a9..ad74039a 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift @@ -40,24 +40,18 @@ import Foundation override open func setupView() { super.setupView() addMolecule(stack) + stack.restack() } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?){ super.set(with: model, delegateObject, additionalData) guard let model = model as? ListOneColumnFullWidthTextAllTextAndLinksModel else { return } - eyebrow.setOptional(with: model.eyebrow, delegateObject, additionalData) - headline.setOptional(with: model.headline, delegateObject, additionalData) - subHeadline.setOptional(with: model.subHeadline, delegateObject, additionalData) - body.setOptional(with: model.body, delegateObject, additionalData) - link.setOptional(with: model.link, delegateObject, additionalData) - - // Hide labels if neeeded. - stack.stackModel?.molecules[0].gone = !eyebrow.hasText - stack.stackModel?.molecules[1].gone = !headline.hasText - stack.stackModel?.molecules[2].gone = !subHeadline.hasText - stack.stackModel?.molecules[3].gone = !body.hasText - stack.stackModel?.molecules[4].gone = (link.titleLabel?.text?.count ?? 0) == 0 - stack.restack() + stack.updateContainedMolecules(with: [model.eyebrow, + model.headline, + model.subHeadline, + model.body, + model.link], + delegateObject, additionalData) } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift new file mode 100644 index 00000000..5e7d39c6 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift @@ -0,0 +1,61 @@ +// +// ListThreeColumnDataUsage.swift +// MVMCoreUI +// +// Created by Kruthika KP on 20/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListThreeColumnDataUsage: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //------------------------------------------------------- + public let leftLabel = Label.createLabelRegularBodySmall(true) + public let centerLabel = Label.createLabelRegularBodySmall(true) + public let rightLabel = Label.createLabelRegularBodySmall(true) + var stack: Stack + + //------------------------------------------------------ + // MARK: - Initializers + //------------------------------------------------------ + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 40, horizontalAlignment: .leading)), + (view: centerLabel, model: StackItemModel(percent: 37, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 23, horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + open override func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + // MARK: - ModelMoleculeViewProtocol + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListThreeColumnDataUsageModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + centerLabel.set(with: model.centerLabel, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 121 + } + + open override func reset() { + super.reset() + leftLabel.styleRegularBodySmall(true) + centerLabel.styleRegularBodySmall(true) + rightLabel.styleRegularBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsageModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsageModel.swift new file mode 100644 index 00000000..56f1e402 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsageModel.swift @@ -0,0 +1,48 @@ +// +// ListThreeColumnDataUsageModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 20/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListThreeColumnDataUsageModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list3CDataUsg" + public var leftLabel: LabelModel + public var centerLabel: LabelModel + public var rightLabel: LabelModel + + public init(leftLabel:LabelModel, centerLabel:LabelModel, rightLabel:LabelModel) { + self.leftLabel = leftLabel + self.centerLabel = centerLabel + self.rightLabel = rightLabel + super.init() + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case centerLabel + case rightLabel + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + centerLabel = try typeContainer.decode(LabelModel.self, forKey: .centerLabel) + rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(leftLabel, forKey: .leftLabel) + try container.encode(centerLabel, forKey: .centerLabel) + try container.encode(rightLabel, forKey: .rightLabel) + } +} + diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift index dc01fd11..1a6bced6 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift @@ -17,6 +17,7 @@ import Foundation private enum CodingKeys: String, CodingKey { case moleculeName + case backgroundColor case spacing case percent case gone @@ -33,6 +34,7 @@ import Foundation if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) { self.gone = gone } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) try super.init(from: decoder) } @@ -43,5 +45,6 @@ import Foundation try container.encodeIfPresent(spacing, forKey: .spacing) try container.encodeIfPresent(percent, forKey: .percent) try container.encode(gone, forKey: .gone) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) } } diff --git a/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift index 9ce24d3b..3ff3a41f 100644 --- a/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift @@ -8,11 +8,19 @@ import UIKit + @objcMembers open class LabelToggle: View { - public let label = Label.commonLabelB1(true) + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public let label = Label.createLabelBoldBodySmall(true) public let toggle = Toggle() + //-------------------------------------------------- // MARK: - MVMCoreViewProtocol + //-------------------------------------------------- + open override func updateView(_ size: CGFloat) { super.updateView(size) label.updateView(size) @@ -21,28 +29,26 @@ import UIKit open override func setupView() { super.setupView() - guard toggle.superview == nil else { - return - } addSubview(label) addSubview(toggle) - label.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) + label.setContentHuggingPriority(.required, for: .vertical) NSLayoutConstraint.pinViews(leftView: label, rightView: toggle, alignTop: false) } - // MARK:- MoleculeViewProtocol open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { guard let model = model as? LabelToggleModel, let toggleHeight = Toggle.estimatedHeight(with: model.toggle, delegateObject), - let labelHeight = Label.estimatedHeight(with: model.label, delegateObject) else { return nil } + let labelHeight = Label.estimatedHeight(with: model.label, delegateObject) + else { return nil } + return max(toggleHeight, labelHeight) } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let labelToggleModel = model as? LabelToggleModel else { - return - } + + guard let labelToggleModel = model as? LabelToggleModel else { return } + label.set(with: labelToggleModel.label, delegateObject, additionalData) toggle.set(with: labelToggleModel.toggle, delegateObject, additionalData) } @@ -52,6 +58,6 @@ import UIKit super.reset() label.reset() toggle.reset() - label.styleB1(true) + label.styleBoldBodySmall(true) } } diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index 82f83530..dd04140b 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -33,6 +33,7 @@ import UIKit stack.stackModel?.spacing = 0 addSubview(stack) NSLayoutConstraint.constraintPinSubview(toSuperview: stack) + stack.restack() } open override func updateView(_ size: CGFloat) { @@ -58,18 +59,11 @@ import UIKit open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - - eyebrow.setOptional(with: castModel?.eyebrow, delegateObject, additionalData) - headline.setOptional(with: castModel?.headline, delegateObject, additionalData) - body.setOptional(with: castModel?.body, delegateObject, additionalData) - link.setOptional(with: castModel?.link, delegateObject, additionalData) - - // Hide labels if neeeded. - stack.stackModel?.molecules[0].gone = !eyebrow.hasText - stack.stackModel?.molecules[1].gone = !headline.hasText - stack.stackModel?.molecules[2].gone = !body.hasText - stack.stackModel?.molecules[3].gone = (link.titleLabel?.text?.count ?? 0) == 0 - stack.restack() + stack.updateContainedMolecules(with: [castModel?.eyebrow, + castModel?.headline, + castModel?.body, + castModel?.link], + delegateObject, additionalData) } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { diff --git a/MVMCoreUI/Atomic/Organisms/Stack.swift b/MVMCoreUI/Atomic/Organisms/Stack.swift index f539cf8b..ad961586 100644 --- a/MVMCoreUI/Atomic/Organisms/Stack.swift +++ b/MVMCoreUI/Atomic/Organisms/Stack.swift @@ -62,6 +62,32 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto } } + /// A convenience function for when the stackItems are containers and we want to update them based on the contained molecules models. If model is nil, stackItem is set to gone. Restacks if necessary. + open func updateContainedMolecules(with models: [MoleculeModelProtocol?], _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard var stackModel = stackModel else { return } + var needsRestack = false + for (index, item) in stackItems.enumerated() { + guard let container = item as? UIView & ContainerProtocol, + let contained = container.view as? MoleculeViewProtocol else { + continue + } + if let model = models[index] { + contained.set(with: model, delegateObject, additionalData) + if stackModel.molecules[index].gone { + stackModel.molecules[index].gone = false + needsRestack = true + } + } else if !stackModel.molecules[index].gone { + stackModel.molecules[index].gone = true + needsRestack = true + } + } + + if needsRestack { + restack() + } + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Organisms/StackModel.swift b/MVMCoreUI/Atomic/Organisms/StackModel.swift index cc8f7d0e..3f13a580 100644 --- a/MVMCoreUI/Atomic/Organisms/StackModel.swift +++ b/MVMCoreUI/Atomic/Organisms/StackModel.swift @@ -48,6 +48,7 @@ import Foundation if let spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) { self.spacing = spacing } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) try super.init(from: decoder) } @@ -58,5 +59,6 @@ import Foundation try container.encodeIfPresent(axis.rawValueString, forKey: .axis) try container.encodeIfPresent(spacing, forKey: .spacing) try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) } } diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift index 2e4eceef..97e83f98 100644 --- a/MVMCoreUI/BaseControllers/ScrollingViewController.swift +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -36,7 +36,7 @@ open class ScrollingViewController: ViewController { super.viewDidLoad() // Adds the tap gesture to dismiss the keyboard. - dismissKeyboardTapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissFieldInput(sender:))) + dismissKeyboardTapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissFieldInput)) view.addGestureRecognizer(dismissKeyboardTapGesture!) dismissKeyboardTapGesture?.isEnabled = false scrollView.alwaysBounceVertical = false diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 677d9e31..b12440a6 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -405,7 +405,7 @@ import UIKit } } - @objc open func dismissFieldInput(sender: Any?) { + @objc open func dismissFieldInput(_ sender: Any?) { selectedField?.resignFirstResponder() } diff --git a/MVMCoreUI/Containers/Views/EntryFieldContainer.swift b/MVMCoreUI/Containers/Views/EntryFieldContainer.swift index 3a8fac64..9ccd1d25 100644 --- a/MVMCoreUI/Containers/Views/EntryFieldContainer.swift +++ b/MVMCoreUI/Containers/Views/EntryFieldContainer.swift @@ -25,9 +25,7 @@ import UIKit /// Total control over the drawn top, bottom, left and right borders. public var disableAllBorders = false { - didSet { - bottomBar?.isHidden = disableAllBorders - } + didSet { bottomBar?.isHidden = disableAllBorders } } private(set) var fieldState: FieldState = .original { diff --git a/MVMCoreUI/Styles/Styler.swift b/MVMCoreUI/Styles/Styler.swift index b5d6627c..509ad107 100644 --- a/MVMCoreUI/Styles/Styler.swift +++ b/MVMCoreUI/Styles/Styler.swift @@ -10,10 +10,8 @@ import Foundation open class Styler { - //-------------------------------------------------- - // MARK: - Enums - //-------------------------------------------------- + // MARK:- Font Enum public enum Font: String, Codable { case Title2XLarge case TitleXLarge diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/Contents.json new file mode 100644 index 00000000..af2434ac --- /dev/null +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "alert_standard @1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "alert_standard @2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "alert_standard @3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @1x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @1x.png new file mode 100644 index 00000000..f5fbe367 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @1x.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @2x.png new file mode 100644 index 00000000..d40e7149 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @2x.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @3x.png new file mode 100644 index 00000000..00589541 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/alert_standard.imageset/alert_standard @3x.png differ diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m index 4ebfd013..d5ad9a83 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m @@ -198,7 +198,7 @@ [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|->=space-[button]->=space-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"space":@(PaddingFive)} views:NSDictionaryOfVariableBindings(button)]]; [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES; [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.centerView attribute:NSLayoutAttributeRight multiplier:1 constant:PaddingThree].active = YES; - [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeRight multiplier:1 constant:(self.closeButton ? PaddingTen : PaddingThree)].active = YES; + [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeRight multiplier:1 constant:(self.closeButton ? PaddingTen : PaddingFive)].active = YES; self.button = button; } } else { @@ -210,7 +210,7 @@ } if (!self.labelRightConstraint) { - self.labelRightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.centerView attribute:NSLayoutAttributeRight multiplier:1 constant:(self.closeButton ? PaddingTen : PaddingThree)]; + self.labelRightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.centerView attribute:NSLayoutAttributeRight multiplier:1 constant:(self.closeButton ? PaddingTen : PaddingFive)]; } self.labelRightConstraint.active = YES; } diff --git a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift index 3ee452b0..704cf801 100644 --- a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift +++ b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift @@ -14,7 +14,7 @@ public extension MVMCoreUICommonViewsUtility { let toolbar = self.makeEmptyToolbar() let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) - let button = UIBarButtonItem(barButtonSystemItem: .done, target: delegate, action: #selector(ObservingTextFieldDelegate.dismissFieldInput(sender:))) + let button = UIBarButtonItem(barButtonSystemItem: .done, target: delegate, action: #selector(ObservingTextFieldDelegate.dismissFieldInput)) button.tintColor = .black toolbar.setItems([space, button], animated: false)