Merge branch 'develop' into feature/dynamicAccessibilityTraits
This commit is contained in:
commit
3d7e1ac646
@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 52;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@ -168,6 +168,10 @@
|
|||||||
526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */; };
|
526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */; };
|
||||||
52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */; };
|
52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */; };
|
||||||
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */; };
|
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */; };
|
||||||
|
608211282AC6B57E00C3FC39 /* MVMCoreUILoggingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */; };
|
||||||
|
7199C8162A4F3A64001568B7 /* AccessibilityHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */; };
|
||||||
|
71BE969E2AD96BE6000B5DB7 /* RotorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */; };
|
||||||
|
608211282AC6B57E00C3FC39 /* MVMCoreUILoggingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */; };
|
||||||
8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */; };
|
8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */; };
|
||||||
8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */; };
|
8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */; };
|
||||||
8D084AD02410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */; };
|
8D084AD02410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */; };
|
||||||
@ -289,12 +293,15 @@
|
|||||||
AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; };
|
AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; };
|
||||||
AF7E509829E477C1009DC2AD /* AlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509629E477C0009DC2AD /* AlertHandler.swift */; };
|
AF7E509829E477C1009DC2AD /* AlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509629E477C0009DC2AD /* AlertHandler.swift */; };
|
||||||
AF7E509929E477C1009DC2AD /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509729E477C0009DC2AD /* AlertController.swift */; };
|
AF7E509929E477C1009DC2AD /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509729E477C0009DC2AD /* AlertController.swift */; };
|
||||||
|
AF8118302AB39B0900FAD1BA /* RawRepresentableCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF81182F2AB39B0900FAD1BA /* RawRepresentableCodable.swift */; };
|
||||||
AFA4932029E5CA73001A9663 /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4931F29E5CA73001A9663 /* AlertOperation.swift */; };
|
AFA4932029E5CA73001A9663 /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4931F29E5CA73001A9663 /* AlertOperation.swift */; };
|
||||||
AFA4932229E5EF2E001A9663 /* NotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */; };
|
AFA4932229E5EF2E001A9663 /* NotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */; };
|
||||||
AFA4933F29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */; };
|
AFA4933F29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */; };
|
||||||
AFA4935729EE3DCC001A9663 /* AlertDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4935629EE3DCC001A9663 /* AlertDelegateProtocol.swift */; };
|
AFA4935729EE3DCC001A9663 /* AlertDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4935629EE3DCC001A9663 /* AlertDelegateProtocol.swift */; };
|
||||||
AFE4A1D127DFB5EE00C458D0 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */; };
|
AFE4A1D127DFB5EE00C458D0 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */; };
|
||||||
AFE4A1D627DFBB6F00C458D0 /* UINavigationController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */; };
|
AFE4A1D627DFBB6F00C458D0 /* UINavigationController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */; };
|
||||||
|
B4CC8FBF29DF34730005D28B /* BadgeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4CC8FBE29DF34730005D28B /* BadgeModel.swift */; };
|
||||||
|
B4CC8FBD29DF34680005D28B /* Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4CC8FBC29DF34680005D28B /* Badge.swift */; };
|
||||||
BB105859248DEFF70069D008 /* UICollectionViewLeftAlignedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */; };
|
BB105859248DEFF70069D008 /* UICollectionViewLeftAlignedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */; };
|
||||||
BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */; };
|
BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */; };
|
||||||
BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */; };
|
BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */; };
|
||||||
@ -468,8 +475,6 @@
|
|||||||
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF26921E6AA0B003B2FB9 /* FLAnimatedImageView.m */; };
|
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF26921E6AA0B003B2FB9 /* FLAnimatedImageView.m */; };
|
||||||
D29DF26E21E6AA0B003B2FB9 /* FLAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF26A21E6AA0B003B2FB9 /* FLAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
D29DF26E21E6AA0B003B2FB9 /* FLAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF26A21E6AA0B003B2FB9 /* FLAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
D29DF26F21E6AA0B003B2FB9 /* FLAnimatedImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF26B21E6AA0B003B2FB9 /* FLAnimatedImageView.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
D29DF26F21E6AA0B003B2FB9 /* FLAnimatedImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF26B21E6AA0B003B2FB9 /* FLAnimatedImageView.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
D29DF27521E79E81003B2FB9 /* MVMCoreUILoggingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF27321E79E81003B2FB9 /* MVMCoreUILoggingHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF27421E79E81003B2FB9 /* MVMCoreUILoggingHandler.m */; };
|
|
||||||
D29DF27921E7A533003B2FB9 /* MVMCoreUISession.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF27721E7A533003B2FB9 /* MVMCoreUISession.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
D29DF27921E7A533003B2FB9 /* MVMCoreUISession.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF27721E7A533003B2FB9 /* MVMCoreUISession.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF27821E7A533003B2FB9 /* MVMCoreUISession.m */; };
|
D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF27821E7A533003B2FB9 /* MVMCoreUISession.m */; };
|
||||||
D29DF28021E7AA51003B2FB9 /* MVMCoreUIDetailViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF27F21E7AA50003B2FB9 /* MVMCoreUIDetailViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
D29DF28021E7AA51003B2FB9 /* MVMCoreUIDetailViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF27F21E7AA50003B2FB9 /* MVMCoreUIDetailViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
@ -571,8 +576,8 @@
|
|||||||
EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */; };
|
EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */; };
|
||||||
EA7E67742758310500ABF773 /* EnableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */; };
|
EA7E67742758310500ABF773 /* EnableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */; };
|
||||||
EA7E67762758365300ABF773 /* UIUpdatableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */; };
|
EA7E67762758365300ABF773 /* UIUpdatableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */; };
|
||||||
EA985C3E2970938F00F2FF2E /* Tilelet.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3D2970938F00F2FF2E /* Tilelet.swift */; };
|
|
||||||
EA985C402970939A00F2FF2E /* TileletModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3F2970939A00F2FF2E /* TileletModel.swift */; };
|
EA985C402970939A00F2FF2E /* TileletModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3F2970939A00F2FF2E /* TileletModel.swift */; };
|
||||||
|
EA985C3E2970938F00F2FF2E /* Tilelet.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3D2970938F00F2FF2E /* Tilelet.swift */; };
|
||||||
EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C5F2970A3F000F2FF2E /* VDS.framework */; };
|
EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C5F2970A3F000F2FF2E /* VDS.framework */; };
|
||||||
EA985C642970A40E00F2FF2E /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C632970A40E00F2FF2E /* VDSTypographyTokens.xcframework */; };
|
EA985C642970A40E00F2FF2E /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C632970A40E00F2FF2E /* VDSTypographyTokens.xcframework */; };
|
||||||
EA985C852981AA9C00F2FF2E /* VDS-Enums+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C842981AA9C00F2FF2E /* VDS-Enums+Codable.swift */; };
|
EA985C852981AA9C00F2FF2E /* VDS-Enums+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C842981AA9C00F2FF2E /* VDS-Enums+Codable.swift */; };
|
||||||
@ -587,6 +592,7 @@
|
|||||||
EAB14BC327D9378D0012AB2C /* RuleAnyModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */; };
|
EAB14BC327D9378D0012AB2C /* RuleAnyModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */; };
|
||||||
EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; };
|
EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; };
|
||||||
EABFC152276913E800E78B40 /* FormLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* FormLabelModel.swift */; };
|
EABFC152276913E800E78B40 /* FormLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* FormLabelModel.swift */; };
|
||||||
|
EACCF38C2ABB346700E0F104 /* VDS-Interpreters.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACCF38B2ABB346700E0F104 /* VDS-Interpreters.swift */; };
|
||||||
FD99130028E21E4900542CC3 /* RuleNotEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */; };
|
FD99130028E21E4900542CC3 /* RuleNotEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
@ -754,6 +760,10 @@
|
|||||||
526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnCompareChanges.swift; sourceTree = "<group>"; };
|
526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnCompareChanges.swift; sourceTree = "<group>"; };
|
||||||
52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAndPaymentMethod.swift; sourceTree = "<group>"; };
|
52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAndPaymentMethod.swift; sourceTree = "<group>"; };
|
||||||
52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAndPaymentMethodModel.swift; sourceTree = "<group>"; };
|
52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAndPaymentMethodModel.swift; sourceTree = "<group>"; };
|
||||||
|
608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingHandler.swift; sourceTree = "<group>"; };
|
||||||
|
7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityHandler.swift; sourceTree = "<group>"; };
|
||||||
|
71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotorHandler.swift; sourceTree = "<group>"; };
|
||||||
|
608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingHandler.swift; sourceTree = "<group>"; };
|
||||||
8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalDataModel.swift; sourceTree = "<group>"; };
|
8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalDataModel.swift; sourceTree = "<group>"; };
|
||||||
8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalData.swift; sourceTree = "<group>"; };
|
8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalData.swift; sourceTree = "<group>"; };
|
||||||
8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextBodyTextModel.swift; sourceTree = "<group>"; };
|
8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextBodyTextModel.swift; sourceTree = "<group>"; };
|
||||||
@ -875,12 +885,15 @@
|
|||||||
AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = "<group>"; };
|
AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = "<group>"; };
|
||||||
AF7E509629E477C0009DC2AD /* AlertHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertHandler.swift; sourceTree = "<group>"; };
|
AF7E509629E477C0009DC2AD /* AlertHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertHandler.swift; sourceTree = "<group>"; };
|
||||||
AF7E509729E477C0009DC2AD /* AlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = "<group>"; };
|
AF7E509729E477C0009DC2AD /* AlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = "<group>"; };
|
||||||
|
AF81182F2AB39B0900FAD1BA /* RawRepresentableCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentableCodable.swift; sourceTree = "<group>"; };
|
||||||
AFA4931F29E5CA73001A9663 /* AlertOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertOperation.swift; sourceTree = "<group>"; };
|
AFA4931F29E5CA73001A9663 /* AlertOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertOperation.swift; sourceTree = "<group>"; };
|
||||||
AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandler.swift; sourceTree = "<group>"; };
|
AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandler.swift; sourceTree = "<group>"; };
|
||||||
AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingDelegateProtocol.swift; sourceTree = "<group>"; };
|
AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingDelegateProtocol.swift; sourceTree = "<group>"; };
|
||||||
AFA4935629EE3DCC001A9663 /* AlertDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertDelegateProtocol.swift; sourceTree = "<group>"; };
|
AFA4935629EE3DCC001A9663 /* AlertDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertDelegateProtocol.swift; sourceTree = "<group>"; };
|
||||||
AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = "<group>"; };
|
AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = "<group>"; };
|
||||||
AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extension.swift"; sourceTree = "<group>"; };
|
AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
B4CC8FBE29DF34730005D28B /* BadgeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeModel.swift; sourceTree = "<group>"; };
|
||||||
|
B4CC8FBC29DF34680005D28B /* Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Badge.swift; sourceTree = "<group>"; };
|
||||||
BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewLeftAlignedLayout.swift; sourceTree = "<group>"; };
|
BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewLeftAlignedLayout.swift; sourceTree = "<group>"; };
|
||||||
BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMediumModel.swift; sourceTree = "<group>"; };
|
BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMediumModel.swift; sourceTree = "<group>"; };
|
||||||
BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMedium.swift; sourceTree = "<group>"; };
|
BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMedium.swift; sourceTree = "<group>"; };
|
||||||
@ -1062,8 +1075,6 @@
|
|||||||
D29DF26921E6AA0B003B2FB9 /* FLAnimatedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLAnimatedImageView.m; sourceTree = "<group>"; };
|
D29DF26921E6AA0B003B2FB9 /* FLAnimatedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLAnimatedImageView.m; sourceTree = "<group>"; };
|
||||||
D29DF26A21E6AA0B003B2FB9 /* FLAnimatedImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImage.h; sourceTree = "<group>"; };
|
D29DF26A21E6AA0B003B2FB9 /* FLAnimatedImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImage.h; sourceTree = "<group>"; };
|
||||||
D29DF26B21E6AA0B003B2FB9 /* FLAnimatedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImageView.h; sourceTree = "<group>"; };
|
D29DF26B21E6AA0B003B2FB9 /* FLAnimatedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImageView.h; sourceTree = "<group>"; };
|
||||||
D29DF27321E79E81003B2FB9 /* MVMCoreUILoggingHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUILoggingHandler.h; sourceTree = "<group>"; };
|
|
||||||
D29DF27421E79E81003B2FB9 /* MVMCoreUILoggingHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUILoggingHandler.m; sourceTree = "<group>"; };
|
|
||||||
D29DF27721E7A533003B2FB9 /* MVMCoreUISession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUISession.h; sourceTree = "<group>"; };
|
D29DF27721E7A533003B2FB9 /* MVMCoreUISession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUISession.h; sourceTree = "<group>"; };
|
||||||
D29DF27821E7A533003B2FB9 /* MVMCoreUISession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUISession.m; sourceTree = "<group>"; };
|
D29DF27821E7A533003B2FB9 /* MVMCoreUISession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUISession.m; sourceTree = "<group>"; };
|
||||||
D29DF27F21E7AA50003B2FB9 /* MVMCoreUIDetailViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIDetailViewProtocol.h; sourceTree = "<group>"; };
|
D29DF27F21E7AA50003B2FB9 /* MVMCoreUIDetailViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIDetailViewProtocol.h; sourceTree = "<group>"; };
|
||||||
@ -1174,6 +1185,7 @@
|
|||||||
EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyModelProtocol.swift; sourceTree = "<group>"; };
|
EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
EABFC1402763BB8D00E78B40 /* FormLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabel.swift; sourceTree = "<group>"; };
|
EABFC1402763BB8D00E78B40 /* FormLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabel.swift; sourceTree = "<group>"; };
|
||||||
EABFC151276913E800E78B40 /* FormLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabelModel.swift; sourceTree = "<group>"; };
|
EABFC151276913E800E78B40 /* FormLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabelModel.swift; sourceTree = "<group>"; };
|
||||||
|
EACCF38B2ABB346700E0F104 /* VDS-Interpreters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VDS-Interpreters.swift"; sourceTree = "<group>"; };
|
||||||
FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleNotEqualsModel.swift; sourceTree = "<group>"; };
|
FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleNotEqualsModel.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@ -1451,6 +1463,15 @@
|
|||||||
path = OneColumn;
|
path = OneColumn;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
7199C8142A4F3A40001568B7 /* Accessibility */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */,
|
||||||
|
71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */,
|
||||||
|
);
|
||||||
|
path = Accessibility;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
8DD1E36C243B3CD900D8F2DF /* ThreeColumn */ = {
|
8DD1E36C243B3CD900D8F2DF /* ThreeColumn */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -1975,6 +1996,7 @@
|
|||||||
D29DF0CE21E404D4003B2FB9 /* MVMCoreUI */ = {
|
D29DF0CE21E404D4003B2FB9 /* MVMCoreUI */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7199C8142A4F3A40001568B7 /* Accessibility */,
|
||||||
01F2C1FC27C81F9700DC3D36 /* Managers */,
|
01F2C1FC27C81F9700DC3D36 /* Managers */,
|
||||||
D2ED27D8254B0C1F00A1C293 /* Alerts */,
|
D2ED27D8254B0C1F00A1C293 /* Alerts */,
|
||||||
27F973512466071600CAB5C5 /* Behaviors */,
|
27F973512466071600CAB5C5 /* Behaviors */,
|
||||||
@ -2152,6 +2174,7 @@
|
|||||||
D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */,
|
D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */,
|
||||||
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */,
|
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */,
|
||||||
0AF60F0826B3316E00AC3DB4 /* MVMCoreUIUtility+Extension.swift */,
|
0AF60F0826B3316E00AC3DB4 /* MVMCoreUIUtility+Extension.swift */,
|
||||||
|
AF81182F2AB39B0900FAD1BA /* RawRepresentableCodable.swift */,
|
||||||
);
|
);
|
||||||
path = Utility;
|
path = Utility;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2215,8 +2238,10 @@
|
|||||||
AA37CBD42519072F0027344C /* Stars.swift */,
|
AA37CBD42519072F0027344C /* Stars.swift */,
|
||||||
AA07EA902510A442009A2AE3 /* StarModel.swift */,
|
AA07EA902510A442009A2AE3 /* StarModel.swift */,
|
||||||
AA07EA922510A451009A2AE3 /* Star.swift */,
|
AA07EA922510A451009A2AE3 /* Star.swift */,
|
||||||
EA985C3D2970938F00F2FF2E /* Tilelet.swift */,
|
B4CC8FBE29DF34730005D28B /* BadgeModel.swift */,
|
||||||
|
B4CC8FBC29DF34680005D28B /* Badge.swift */,
|
||||||
EA985C3F2970939A00F2FF2E /* TileletModel.swift */,
|
EA985C3F2970939A00F2FF2E /* TileletModel.swift */,
|
||||||
|
EA985C3D2970938F00F2FF2E /* Tilelet.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2266,8 +2291,7 @@
|
|||||||
D2B18B912361E65A00A9AEDC /* CoreUIObject.swift */,
|
D2B18B912361E65A00A9AEDC /* CoreUIObject.swift */,
|
||||||
D29DF27721E7A533003B2FB9 /* MVMCoreUISession.h */,
|
D29DF27721E7A533003B2FB9 /* MVMCoreUISession.h */,
|
||||||
D29DF27821E7A533003B2FB9 /* MVMCoreUISession.m */,
|
D29DF27821E7A533003B2FB9 /* MVMCoreUISession.m */,
|
||||||
D29DF27321E79E81003B2FB9 /* MVMCoreUILoggingHandler.h */,
|
608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */,
|
||||||
D29DF27421E79E81003B2FB9 /* MVMCoreUILoggingHandler.m */,
|
|
||||||
AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */,
|
AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */,
|
||||||
D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */,
|
D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */,
|
||||||
D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */,
|
D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */,
|
||||||
@ -2415,6 +2439,7 @@
|
|||||||
011B58EE23A2AA850085F53C /* ModelProtocols */,
|
011B58EE23A2AA850085F53C /* ModelProtocols */,
|
||||||
27559EFB27D691D3000836C1 /* ViewMaskingProtocol.swift */,
|
27559EFB27D691D3000836C1 /* ViewMaskingProtocol.swift */,
|
||||||
EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */,
|
EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */,
|
||||||
|
EACCF38B2ABB346700E0F104 /* VDS-Interpreters.swift */,
|
||||||
);
|
);
|
||||||
path = Protocols;
|
path = Protocols;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2504,7 +2529,6 @@
|
|||||||
D29DF25921E6A22D003B2FB9 /* MFButtonProtocol.h in Headers */,
|
D29DF25921E6A22D003B2FB9 /* MFButtonProtocol.h in Headers */,
|
||||||
D29DF28421E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.h in Headers */,
|
D29DF28421E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.h in Headers */,
|
||||||
D29DF2CE21E7C104003B2FB9 /* MFLoadingViewController.h in Headers */,
|
D29DF2CE21E7C104003B2FB9 /* MFLoadingViewController.h in Headers */,
|
||||||
D29DF27521E79E81003B2FB9 /* MVMCoreUILoggingHandler.h in Headers */,
|
|
||||||
D29DF2B321E7B76D003B2FB9 /* MFLoadingSpinner.h in Headers */,
|
D29DF2B321E7B76D003B2FB9 /* MFLoadingSpinner.h in Headers */,
|
||||||
D20492A424329A2800A5EED6 /* MVMCoreUIPagingProtocol.h in Headers */,
|
D20492A424329A2800A5EED6 /* MVMCoreUIPagingProtocol.h in Headers */,
|
||||||
D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */,
|
D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */,
|
||||||
@ -2729,6 +2753,7 @@
|
|||||||
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */,
|
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */,
|
||||||
D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */,
|
D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */,
|
||||||
D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */,
|
D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */,
|
||||||
|
608211282AC6B57E00C3FC39 /* MVMCoreUILoggingHandler.swift in Sources */,
|
||||||
014AA72F23C5059B006F3E93 /* ThreeLayerPageTemplateModel.swift in Sources */,
|
014AA72F23C5059B006F3E93 /* ThreeLayerPageTemplateModel.swift in Sources */,
|
||||||
0A21DB91235E0EDB00C160A2 /* DigitBox.swift in Sources */,
|
0A21DB91235E0EDB00C160A2 /* DigitBox.swift in Sources */,
|
||||||
BBAA4F04243D8E3B005AAD5F /* RadioBoxModel.swift in Sources */,
|
BBAA4F04243D8E3B005AAD5F /* RadioBoxModel.swift in Sources */,
|
||||||
@ -2736,7 +2761,6 @@
|
|||||||
D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */,
|
D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */,
|
||||||
AAA905E124D1759A00D1EFAB /* ListThreeColumnBillHistory.swift in Sources */,
|
AAA905E124D1759A00D1EFAB /* ListThreeColumnBillHistory.swift in Sources */,
|
||||||
C7F8012323E846C300396FBD /* ListRVWheelModel.swift in Sources */,
|
C7F8012323E846C300396FBD /* ListRVWheelModel.swift in Sources */,
|
||||||
D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */,
|
|
||||||
D2ED27EC254B0CE700A1C293 /* UIAlertControllerStyle+Extension.swift in Sources */,
|
D2ED27EC254B0CE700A1C293 /* UIAlertControllerStyle+Extension.swift in Sources */,
|
||||||
C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */,
|
C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */,
|
||||||
014AA72D23C5059B006F3E93 /* StackPageTemplateModel.swift in Sources */,
|
014AA72D23C5059B006F3E93 /* StackPageTemplateModel.swift in Sources */,
|
||||||
@ -2746,6 +2770,7 @@
|
|||||||
0AE98BAF23FEF956004C5109 /* ExternalLink.swift in Sources */,
|
0AE98BAF23FEF956004C5109 /* ExternalLink.swift in Sources */,
|
||||||
012A88C4238D86E600FE3DA1 /* CarouselItemModelProtocol.swift in Sources */,
|
012A88C4238D86E600FE3DA1 /* CarouselItemModelProtocol.swift in Sources */,
|
||||||
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */,
|
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */,
|
||||||
|
AF8118302AB39B0900FAD1BA /* RawRepresentableCodable.swift in Sources */,
|
||||||
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */,
|
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */,
|
||||||
D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */,
|
D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */,
|
||||||
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */,
|
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */,
|
||||||
@ -2830,6 +2855,7 @@
|
|||||||
0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */,
|
0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */,
|
||||||
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */,
|
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */,
|
||||||
AA3561AC24C9684400452EB1 /* ListRightVariableRightCaretAllTextAndLinksModel.swift in Sources */,
|
AA3561AC24C9684400452EB1 /* ListRightVariableRightCaretAllTextAndLinksModel.swift in Sources */,
|
||||||
|
7199C8162A4F3A64001568B7 /* AccessibilityHandler.swift in Sources */,
|
||||||
D209234F244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift in Sources */,
|
D209234F244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift in Sources */,
|
||||||
525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */,
|
525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */,
|
||||||
32D2609624C19E2100B56344 /* LockupsPlanSMLXL.swift in Sources */,
|
32D2609624C19E2100B56344 /* LockupsPlanSMLXL.swift in Sources */,
|
||||||
@ -2916,6 +2942,7 @@
|
|||||||
27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */,
|
27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */,
|
||||||
D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */,
|
D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */,
|
||||||
526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */,
|
526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */,
|
||||||
|
B4CC8FBD29DF34680005D28B /* Badge.swift in Sources */,
|
||||||
8D24041523E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift in Sources */,
|
8D24041523E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift in Sources */,
|
||||||
3265B30224BCA737000D154B /* HeadersH1NoButtonsBodyTextModel.swift in Sources */,
|
3265B30224BCA737000D154B /* HeadersH1NoButtonsBodyTextModel.swift in Sources */,
|
||||||
D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */,
|
D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */,
|
||||||
@ -2972,6 +2999,7 @@
|
|||||||
D28BA741248025A300B75CB8 /* TabBarModel.swift in Sources */,
|
D28BA741248025A300B75CB8 /* TabBarModel.swift in Sources */,
|
||||||
D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */,
|
D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */,
|
||||||
D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */,
|
D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */,
|
||||||
|
EACCF38C2ABB346700E0F104 /* VDS-Interpreters.swift in Sources */,
|
||||||
C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */,
|
C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */,
|
||||||
0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */,
|
0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */,
|
||||||
D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */,
|
D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */,
|
||||||
@ -3000,6 +3028,7 @@
|
|||||||
D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */,
|
D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */,
|
||||||
D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */,
|
D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */,
|
||||||
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */,
|
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */,
|
||||||
|
71BE969E2AD96BE6000B5DB7 /* RotorHandler.swift in Sources */,
|
||||||
AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */,
|
AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */,
|
||||||
8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */,
|
8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */,
|
||||||
D243859923A16B1800332775 /* Container.swift in Sources */,
|
D243859923A16B1800332775 /* Container.swift in Sources */,
|
||||||
@ -3023,6 +3052,7 @@
|
|||||||
22B678F929E7944E00CF4196 /* GetNotificationAuthStatusBehavior.swift in Sources */,
|
22B678F929E7944E00CF4196 /* GetNotificationAuthStatusBehavior.swift in Sources */,
|
||||||
BB2FB3BB247E7EBC00DF73CD /* TagCollectionViewCell.swift in Sources */,
|
BB2FB3BB247E7EBC00DF73CD /* TagCollectionViewCell.swift in Sources */,
|
||||||
012A88C6238DA34000FE3DA1 /* ModuleMoleculeModel.swift in Sources */,
|
012A88C6238DA34000FE3DA1 /* ModuleMoleculeModel.swift in Sources */,
|
||||||
|
B4CC8FBF29DF34730005D28B /* BadgeModel.swift in Sources */,
|
||||||
94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */,
|
94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */,
|
||||||
EA985C852981AA9C00F2FF2E /* VDS-Enums+Codable.swift in Sources */,
|
EA985C852981AA9C00F2FF2E /* VDS-Enums+Codable.swift in Sources */,
|
||||||
AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */,
|
AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */,
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1500"
|
|
||||||
version = "1.7">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "D29DF0CB21E404D4003B2FB9"
|
|
||||||
BuildableName = "MVMCoreUI.framework"
|
|
||||||
BlueprintName = "MVMCoreUI"
|
|
||||||
ReferencedContainer = "container:MVMCoreUI.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
shouldAutocreateTestPlan = "YES">
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "D29DF0CB21E404D4003B2FB9"
|
|
||||||
BuildableName = "MVMCoreUI.framework"
|
|
||||||
BlueprintName = "MVMCoreUI"
|
|
||||||
ReferencedContainer = "container:MVMCoreUI.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
275
MVMCoreUI/Accessibility/AccessibilityHandler.swift
Normal file
275
MVMCoreUI/Accessibility/AccessibilityHandler.swift
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
//
|
||||||
|
// AccessibilityHandler.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 30/06/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import MVMCore
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
//MARK: - AccessibilityOperation
|
||||||
|
public class AccessibilityOperation: MVMCoreOperation {
|
||||||
|
|
||||||
|
private let argument: Any?
|
||||||
|
private let notificationType: UIAccessibility.Notification
|
||||||
|
|
||||||
|
public init(notificationType: UIAccessibility.Notification, argument: Any?) {
|
||||||
|
self.notificationType = notificationType
|
||||||
|
self.argument = argument
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method will post accessibility notification.
|
||||||
|
*/
|
||||||
|
public override func main() {
|
||||||
|
guard !checkAndHandleForCancellation() else { return }
|
||||||
|
guard UIAccessibility.isVoiceOverRunning else {
|
||||||
|
markAsFinished()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Task { @MainActor [weak self] in
|
||||||
|
guard let self = self, !self.isCancelled else {
|
||||||
|
self?.markAsFinished()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
UIAccessibility.post(notification: self.notificationType, argument: self.argument)
|
||||||
|
self.markAsFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - AccessibilityHandler
|
||||||
|
/**
|
||||||
|
AccessibilityHandler will observe the page visibility of every view controller and post notification to the first interactive element on the screen.
|
||||||
|
If we have to shift/focus custom element on the screen on controller shown then we need to pass accessibilityId in the page response.
|
||||||
|
*/
|
||||||
|
open class AccessibilityHandler {
|
||||||
|
|
||||||
|
public static func shared() -> Self? {
|
||||||
|
guard let shared = CoreUIObject.sharedInstance()?.accessibilityHandler else { return nil }
|
||||||
|
return MVMCoreActionUtility.fatalClassCheck(object: shared)
|
||||||
|
}
|
||||||
|
//TODO: Revisit to avoid state properties to store in handler.
|
||||||
|
public var accessibilityId: String? ///This property is used to post accessibility to the UIElement mapped to this accessibilityId
|
||||||
|
public var previousAccessiblityElement: Any? ///This property is capture accessiblity element
|
||||||
|
public var anyCancellable: Set<AnyCancellable> = []
|
||||||
|
public weak var currentController: UIViewController? { MVMCoreUIUtility.getCurrentVisibleController() }
|
||||||
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
//TODO: Revisit to identify the page has top notification or not.
|
||||||
|
private var hasTopNotificationInPage: Bool { (currentController as? MVMCoreViewControllerProtocol)?.loadObject??.responseJSON?.optionalDictionaryForKey("TopNotification") != nil || (currentController as? MVMCoreViewControllerProtocol)?.loadObject??.responseInfoMap?.optionalStringForKey("messageStyle") != nil }
|
||||||
|
private let accessibilityOperationQueue: OperationQueue = {
|
||||||
|
let queue = OperationQueue()
|
||||||
|
queue.maxConcurrentOperationCount = 1
|
||||||
|
return queue
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var rotorHandler = RotorHandler(accessibilityHandler: self)
|
||||||
|
|
||||||
|
/**
|
||||||
|
init method will register for focus changes
|
||||||
|
*/
|
||||||
|
public init() {
|
||||||
|
registerForFocusChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method will capture current focused element
|
||||||
|
*/
|
||||||
|
open func capturePreviousFocusElement() {
|
||||||
|
previousAccessiblityElement = UIAccessibility.focusedElement(using: .notificationVoiceOver)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method will post accessibility notification to previous captured element
|
||||||
|
*/
|
||||||
|
open func postAccessibilityToPrevElement() {
|
||||||
|
post(notification: .layoutChanged, argument: previousAccessiblityElement)
|
||||||
|
previousAccessiblityElement = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method will check if voice over is running then will post notification to the mentioned argument
|
||||||
|
*/
|
||||||
|
public func post(notification type: UIAccessibility.Notification, argument: Any? = nil) {
|
||||||
|
guard UIAccessibility.isVoiceOverRunning else { return }
|
||||||
|
let AccessibilityOperation = AccessibilityOperation(notificationType: type, argument: argument)
|
||||||
|
accessibilityOperationQueue.addOperation(AccessibilityOperation)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method return first focused element from the screen.
|
||||||
|
If navigationBar is hidden then we are returning nil so that voice over will shift to the first interactive element.
|
||||||
|
*/
|
||||||
|
open func getFirstFocusedElementOnScreen() -> Any? {
|
||||||
|
((currentController as? PageProtocol)?.pageModel?.navigationBar?.hidden ?? false) ? nil : currentController?.navigationController?.navigationBar
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is used to decide if AccessibilityHandler can post screen change notification or specific classes will take care of posting Accessibility notification
|
||||||
|
*/
|
||||||
|
open func canPostAccessibilityNotification(for viewController: UIViewController) -> Bool { true }
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is used to identify the UIElement that is mapped to accessibilityId from server response.
|
||||||
|
*/
|
||||||
|
func getPreDefinedFocusedElementIfAny() -> UIView? {
|
||||||
|
guard let accessibilityId, let view = currentController?.view else { return nil }
|
||||||
|
return MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [view]).first {
|
||||||
|
$0.model?.id == accessibilityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AccessibilityHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is used to notify rotor handler about page loaded and capturing the accessibilityId if any.
|
||||||
|
*/
|
||||||
|
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
previousAccessiblityElement = nil
|
||||||
|
rotorHandler.onPageNew(rootMolecules: rootMolecules, delegateObject)
|
||||||
|
guard let loadObject = (delegateObject?.loadDelegate as? MVMCoreViewControllerProtocol)?.loadObject else { return }
|
||||||
|
accessibilityId = loadObject?.pageJSON?.optionalStringForKey("accessibilityId")
|
||||||
|
self.delegateObject = delegateObject
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is used to capture accessibility views on the screen.
|
||||||
|
If the page has accessibilityId, then it will not post any accessibility notification because respective UI mapped element can be identified only on page shown.
|
||||||
|
If it has top notification then we are capturing the first focused element and will not post any accessibility notification.
|
||||||
|
If page doesn't have any top notification or accessibilityId then it will post notification to shift focus to first focused element on the screen.
|
||||||
|
*/
|
||||||
|
public func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
updateAccessibilityViews(delegateObject)
|
||||||
|
guard let controller = delegateObject?.moleculeDelegate as? UIViewController,
|
||||||
|
canPostAccessibilityNotification(for: controller),
|
||||||
|
accessibilityId == nil else { return }
|
||||||
|
if hasTopNotificationInPage {
|
||||||
|
previousAccessiblityElement = getFirstFocusedElementOnScreen()
|
||||||
|
} else {
|
||||||
|
post(notification: .layoutChanged, argument: getFirstFocusedElementOnScreen())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is used to notify rotor handler about page visibility
|
||||||
|
Temp fix: - We are resetting the view accessibilityElements when focus shifted to first focused element on the screen not to have voice over struck in between view elements.
|
||||||
|
https://developer.apple.com/forums/thread/655359
|
||||||
|
https://developer.apple.com/forums/thread/675427
|
||||||
|
If the page has accessibilityId, i.e if server decides to manually shift focus to one of the UIElement then we are identifying the id mapped UIElement & shifting focus to that element.
|
||||||
|
If we have top notification as well in the page then we take that as priority and holding the server driven UIElement in previousAccessiblityElement and post accessibility notification.
|
||||||
|
https://oneconfluence.verizon.com/display/MFD/Accessibility+-+Focus+Retain
|
||||||
|
*/
|
||||||
|
public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
defer {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
(delegateObject?.moleculeDelegate as? UIViewController)?.view.accessibilityElements = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rotorHandler.onPageShown(delegateObject)
|
||||||
|
guard let accessibilityElement = getPreDefinedFocusedElementIfAny() else { return }
|
||||||
|
accessibilityId = nil
|
||||||
|
if hasTopNotificationInPage {
|
||||||
|
previousAccessiblityElement = accessibilityElement
|
||||||
|
} else {
|
||||||
|
post(notification: .layoutChanged, argument: accessibilityElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is used to set view elements as accessibilityElements due to the accessibility behaviour change when new controller is pushed on navigation stack from iOS13+
|
||||||
|
*/
|
||||||
|
private func updateAccessibilityViews(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
guard var currentController = delegateObject?.moleculeDelegate as? UIViewController,
|
||||||
|
currentController.navigationController != nil else { return }///If no navigationController, we can go with the default behaviour of Accessibility voiceover.
|
||||||
|
var accessibilityElements: [Any?] = [MVMCoreUISplitViewController.main()?.topAlertView, MVMCoreUISplitViewController.main()?.navigationController]
|
||||||
|
if let manager = ((delegateObject?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? MVMCoreViewManagerProtocol & UIViewController),
|
||||||
|
let managerAccessibilityElements = manager.getAccessibilityElements() {
|
||||||
|
accessibilityElements.append(contentsOf: managerAccessibilityElements)
|
||||||
|
accessibilityElements.append(contentsOf: manager.view.subviews)
|
||||||
|
currentController = manager
|
||||||
|
} else {
|
||||||
|
accessibilityElements.append(contentsOf: currentController.view.subviews)
|
||||||
|
}
|
||||||
|
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
||||||
|
currentController.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - AccessibilityHandler listeners
|
||||||
|
@objc extension AccessibilityHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method listens for focus changes.
|
||||||
|
When focus is changed manually then we are cancelling existing operations.
|
||||||
|
*/
|
||||||
|
private func registerForFocusChanges() {
|
||||||
|
//Since focus shifted to other elements cancelling existing focus shift notifications if any
|
||||||
|
NotificationCenter.default.publisher(for: UIAccessibility.elementFocusedNotification)
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
self?.accessibilityOperationQueue.cancelAllOperations()
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method listens for top notification changes.
|
||||||
|
When top notification is about to display it will capture previous focused element.
|
||||||
|
When top notification is displayed it will post notification to that notification view
|
||||||
|
When top notification is about to dismiss then it will post announcement that top alert is closed.
|
||||||
|
When top notification is dimissed then it will post notification back to previously captured element.
|
||||||
|
*/
|
||||||
|
func registerForTopNotificationsChanges() {
|
||||||
|
NotificationHandler.shared()?.onNotificationWillShow
|
||||||
|
.sink { [weak self] (_, model) in
|
||||||
|
if self?.previousAccessiblityElement == nil {
|
||||||
|
self?.capturePreviousFocusElement()
|
||||||
|
}
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
NotificationHandler.shared()?.onNotificationShown
|
||||||
|
.sink { [weak self] (view, model) in
|
||||||
|
self?.post(notification: .layoutChanged, argument: view)
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
NotificationHandler.shared()?.onNotificationWillDismiss
|
||||||
|
.sink { [weak self] (view, model) in
|
||||||
|
self?.post(notification: .announcement, argument: MVMCoreUIUtility.hardcodedString(withKey: "AccTopAlertClosed"))
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
NotificationHandler.shared()?.onNotificationDismissed
|
||||||
|
.sink { [weak self] (view, model) in
|
||||||
|
self?.postAccessibilityToPrevElement()
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - AccessibilityHandlerBehaviour
|
||||||
|
/**
|
||||||
|
To notify AccessibilityHandler about the page visibility changes
|
||||||
|
*/
|
||||||
|
//TODO: Revisit why we need a behavior as a notifier.
|
||||||
|
open class AccessibilityHandlerBehavior: PageVisibilityBehavior, PageMoleculeTransformationBehavior {
|
||||||
|
|
||||||
|
public let accessibilityHandler: AccessibilityHandler?
|
||||||
|
|
||||||
|
public init(accessibilityHandler: AccessibilityHandler?) {
|
||||||
|
self.accessibilityHandler = accessibilityHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler = AccessibilityHandler.shared() //Protocol Mandatory init method.
|
||||||
|
}
|
||||||
|
|
||||||
|
open func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler?.onPageNew(rootMolecules: rootMolecules, delegateObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler?.willShowPage(delegateObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler?.onPageShown(delegateObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
357
MVMCoreUI/Accessibility/RotorHandler.swift
Normal file
357
MVMCoreUI/Accessibility/RotorHandler.swift
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
//
|
||||||
|
// RotorHandler.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 13/10/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
//MARK: - RotorType that our app supports
|
||||||
|
fileprivate enum RotorType: String, CaseIterable {
|
||||||
|
|
||||||
|
case button = "Buttons"
|
||||||
|
case header = "Header"
|
||||||
|
case link = "Link"
|
||||||
|
|
||||||
|
var trait: UIAccessibilityTraits {
|
||||||
|
switch self {
|
||||||
|
case .button:
|
||||||
|
return .button
|
||||||
|
case .header:
|
||||||
|
return .header
|
||||||
|
case .link:
|
||||||
|
return .link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Filter block on model elements based on rotor type
|
||||||
|
var modelFilter: ((MoleculeModelProtocol) -> Bool) {
|
||||||
|
switch self {
|
||||||
|
default:
|
||||||
|
return { $0.accessibilityTraits?.contains(trait) ?? false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Filter block on model UIElements based on rotor type
|
||||||
|
var filter: ((UIView) -> Bool) {
|
||||||
|
switch self {
|
||||||
|
default:
|
||||||
|
return { $0.accessibilityTraits.contains(trait) && !$0.isHidden }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns custom rotor of the specific type
|
||||||
|
func getUIAccessibilityCustomRotor(itemSearch: @escaping UIAccessibilityCustomRotor.Search) -> UIAccessibilityCustomRotor? {
|
||||||
|
var accessibilityCustomRotor: UIAccessibilityCustomRotor?
|
||||||
|
switch self {
|
||||||
|
case .header:
|
||||||
|
accessibilityCustomRotor = UIAccessibilityCustomRotor(systemType: .heading, itemSearch: itemSearch)
|
||||||
|
default:
|
||||||
|
accessibilityCustomRotor = UIAccessibilityCustomRotor(name: rawValue, itemSearch: itemSearch)
|
||||||
|
}
|
||||||
|
return accessibilityCustomRotor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - RotorViewElementsProtocol
|
||||||
|
public protocol RotorViewElementsProtocol: MVMCoreViewControllerProtocol {
|
||||||
|
var topView: UIView? { get set }
|
||||||
|
var middleView: UIView? { get set }
|
||||||
|
var bottomView: UIView? { get set }
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - RotorProtocols
|
||||||
|
public protocol RotorScrollDelegateProtocol: MVMCoreViewControllerProtocol {
|
||||||
|
func scrollRectToVisible(_ rect: CGRect, animated: Bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol RotorListTypeDelegateProtocol: RotorScrollDelegateProtocol {
|
||||||
|
func scrollToRow(at indexPath: IndexPath, animated: Bool) -> Void
|
||||||
|
func cellForRow(at indexPath: IndexPath) -> UIView?
|
||||||
|
func getIndexPathFor(molecule: MoleculeModelProtocol) -> IndexPath?
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - Rotor Element - RotorElement info when we are traversing on model elements in the page.
|
||||||
|
struct RotorElement {
|
||||||
|
|
||||||
|
let indexPath: IndexPath?
|
||||||
|
let model: MoleculeModelProtocol
|
||||||
|
let carouselItemModel: MoleculeModelProtocol?///This element is the parent of model item if we have rotor element inside carousel. This is used to scroll to specific carousel item when rotor mode is enabled.
|
||||||
|
|
||||||
|
init(indexPath: IndexPath? = nil, model: MoleculeModelProtocol, carouselItemModel: MoleculeModelProtocol? = nil) {
|
||||||
|
self.indexPath = indexPath
|
||||||
|
self.model = model
|
||||||
|
self.carouselItemModel = carouselItemModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - Rotor Handler
|
||||||
|
class RotorHandler {
|
||||||
|
|
||||||
|
private var rotorIndexes: [RotorType: Int] = [:]
|
||||||
|
private var rotorElements: [RotorType: [Any]] = [:]
|
||||||
|
private var anyCancellable: Set<AnyCancellable> = []
|
||||||
|
private weak var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
private weak var delegate: MVMCoreViewControllerProtocol? { delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol }
|
||||||
|
private weak var accessibilityHandler: AccessibilityHandler?
|
||||||
|
|
||||||
|
init(accessibilityHandler: AccessibilityHandler?) {
|
||||||
|
self.accessibilityHandler = accessibilityHandler
|
||||||
|
registerForVoiceOverChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
///Preparing rotors when accessibility voiceover is turned after page is loaded.
|
||||||
|
private func registerForVoiceOverChanges() {
|
||||||
|
NotificationCenter.default.publisher(for: UIAccessibility.voiceOverStatusDidChangeNotification)
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
guard UIAccessibility.isVoiceOverRunning, (self?.rotorElements.isEmpty ?? true) else { return }
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self?.identifyAndPrepareRotors(self?.delegateObject)
|
||||||
|
}
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - Pagevisibility behaviour methods.
|
||||||
|
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
rotorIndexes = [:]
|
||||||
|
rotorElements = [:]
|
||||||
|
self.delegateObject = delegateObject
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
identifyAndPrepareRotors(delegateObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - Rotor methods
|
||||||
|
/**
|
||||||
|
This method prepares custom rotors that will be assigned to the current controller.
|
||||||
|
Rotor will be created only if the page contains that trait mapped element or the conditions met.
|
||||||
|
*/
|
||||||
|
private func identifyAndPrepareRotors(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
guard UIAccessibility.isVoiceOverRunning,
|
||||||
|
let currentViewController = (delegateObject?.moleculeDelegate as? (MVMCoreViewControllerProtocol & UIViewController)) else { return }
|
||||||
|
var customRotors: [UIAccessibilityCustomRotor] = []
|
||||||
|
for type in RotorType.allCases {
|
||||||
|
if let elements = getTraitMappedElements(currentViewController, type: type),
|
||||||
|
!elements.isEmpty,
|
||||||
|
let rotor = createRotor(for: type) {
|
||||||
|
rotorElements[type] = elements
|
||||||
|
customRotors.append(rotor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentViewController.view.accessibilityCustomRotors = customRotors
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method prepares trait mapped elements of the current controller and from its manager if exists.
|
||||||
|
*/
|
||||||
|
private func getTraitMappedElements(_ template: (MVMCoreViewControllerProtocol & UIViewController)?, type: RotorType) -> [Any]? {
|
||||||
|
var accessibilityElements: [Any?] = []
|
||||||
|
if let manager = (template as? MVMCoreViewManagerViewControllerProtocol)?.manager as? (MVMCoreViewControllerProtocol & UIViewController) {
|
||||||
|
accessibilityElements.append(contentsOf: getRotorElementsFrom(manager: manager, type: type) ?? [])
|
||||||
|
}
|
||||||
|
if let rotorElements = getRotorElementsFrom(template: template, type: type) {
|
||||||
|
accessibilityElements.append(contentsOf: rotorElements)
|
||||||
|
}
|
||||||
|
if let tabBarHidden = (template as? TabPageModelProtocol)?.tabBarHidden, !tabBarHidden {
|
||||||
|
accessibilityElements.append(contentsOf: (MVMCoreUISplitViewController.main()?.tabBar?.subviews ?? []).filter { type.filter($0) })
|
||||||
|
}
|
||||||
|
return accessibilityElements.compactMap { $0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method prepares trait mapped elements from manager
|
||||||
|
*/
|
||||||
|
private func getRotorElementsFrom(manager: (MVMCoreViewControllerProtocol & UIViewController)?, type: RotorType) -> [Any]? {
|
||||||
|
guard let elements = (manager as? MVMCoreViewManagerProtocol)?.getAccessibilityElements() as? [UIView] else { return nil }
|
||||||
|
return MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: elements).filter { type.filter($0) } as [Any]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method prepares triat mapped elements from the current viewcontroller.
|
||||||
|
For BAU pages: This will traverse through the view hierarchy for trait mapped elements.
|
||||||
|
For Molecular pages: This will traverse through the models to identify the trait mapped model. Along with traversed models, again we are traversing on view hierarchy if any subViews are added to view to get trait mapped elements.
|
||||||
|
*/
|
||||||
|
private func getRotorElementsFrom(template: (MVMCoreViewControllerProtocol & UIViewController)?, type: RotorType) -> [Any]? {
|
||||||
|
guard let template = template as? (RotorViewElementsProtocol & MVMCoreViewControllerProtocol & ViewController) else {
|
||||||
|
return MVMCoreUIUtility.findViews(by: UIView.self, views: [template?.view].compactMap { $0 }).filter { type.filter($0) } as [Any] //BAU pages
|
||||||
|
}
|
||||||
|
let rotorElements = getRotorElements(from: template, type: type)
|
||||||
|
let remainingRotorElements = MVMCoreUIUtility.findViews(by: UIView.self, views: [template.view], excludedViews: [template.topView, template.middleView, template.bottomView].compactMap { $0 }).filter { type.filter($0) } as [Any] //Other elements added to view if any.
|
||||||
|
return rotorElements + remainingRotorElements
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is to get rotor elements form Molecular pages only.
|
||||||
|
We are filtering out header, molecules, footer from rootMolcules because rootMolcules may or maynot have the correct order of molecule models(header, body, footer). If we don't maintain order, Voiceover might first go footer then body elements then header.(Safety step)
|
||||||
|
*/
|
||||||
|
private func getRotorElements(from template: (MVMCoreViewControllerProtocol & ViewController & RotorViewElementsProtocol)?, type: RotorType) -> [Any] {
|
||||||
|
guard let templateModel = template?.model as? ThreeLayerModelBase else { return [] }
|
||||||
|
var rotorElements: [Any] = []
|
||||||
|
if let headerView = template?.topView {
|
||||||
|
rotorElements.append(contentsOf: MVMCoreUIUtility.findViews(by: UIView.self, views: [headerView].compactMap { $0 }).filter { type.filter($0) })
|
||||||
|
}
|
||||||
|
let molecules = templateModel.rootMolecules.filter { !($0.id == templateModel.header?.id || $0.id == templateModel.footer?.id) }
|
||||||
|
rotorElements.append(contentsOf: getRotorElements(from: molecules, template: template, type: type))
|
||||||
|
if let bottomView = template?.bottomView {
|
||||||
|
rotorElements.append(contentsOf: MVMCoreUIUtility.findViews(by: UIView.self, views: [bottomView].compactMap { $0 }).filter { type.filter($0) })
|
||||||
|
}
|
||||||
|
return rotorElements
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method actually travers through the molecules and identify triat mapped model element along with indexPath of the element if its List/Collection templates.
|
||||||
|
Along with model, indexPath we are also capturing carouselItemModel because when we have Carousel inside the molecule, we need to scroll to carousel item if we have trait mapped rotor element inside the Carousel - (Multi Scroll behaviour)
|
||||||
|
Identiying the CarouselModel, CarouselItemModel by depth.
|
||||||
|
*/
|
||||||
|
private func getRotorElements(from molecules: [MoleculeModelProtocol], template: (MVMCoreViewControllerProtocol & ViewController)?, type: RotorType) -> [Any] {
|
||||||
|
var traitIndexPath: IndexPath?
|
||||||
|
var carouselItemModel: MoleculeModelProtocol?
|
||||||
|
var carouselDepth: Int?
|
||||||
|
let rotorElements: [RotorElement] = molecules.reduceDepthFirstTraverse(options: .parentFirst, depth: 0, initialResult: []) { result, model, depth in
|
||||||
|
if let listModel = model as? (ListItemModelProtocol & MoleculeModelProtocol),
|
||||||
|
let indexPath = (template as? RotorListTypeDelegateProtocol)?.getIndexPathFor(molecule: listModel) {
|
||||||
|
traitIndexPath = indexPath
|
||||||
|
}
|
||||||
|
if let _carouselDepth = carouselDepth, depth < _carouselDepth {
|
||||||
|
carouselDepth = nil
|
||||||
|
carouselItemModel = nil
|
||||||
|
}
|
||||||
|
if model is CarouselModel { carouselDepth = depth }
|
||||||
|
carouselItemModel = model is CarouselItemModelProtocol ? model : carouselItemModel
|
||||||
|
var result = result
|
||||||
|
if type.modelFilter(model) {
|
||||||
|
result.append(.init(indexPath: traitIndexPath, model: model, carouselItemModel: carouselItemModel))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return rotorElements as [Any]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method creates a rotor based on the RotorType.
|
||||||
|
UIAccessibilityCustomRotor.Search Predicate block is used to return the current rotor UI element
|
||||||
|
If the rotor element is of type UIElement(subclasses of UIView) then it will post the accessibility notification directly to that UI element and scrolling that element to the visible area.
|
||||||
|
If rotor element is of type RotorElement then
|
||||||
|
1. If we have indexPath in rotorElement then the template will scroll (list/collection) to that indexPath, and try to capture the cell view.
|
||||||
|
2. After the cell view is captured, traversing the cell hierarchy which matches the trait & id of that view's model.
|
||||||
|
3. After identifying the element, then will post the accessibility notification directly to that UI element
|
||||||
|
If we have carouselItemModel in RotorElement then
|
||||||
|
1. If we have indexPath in rotorElement then the template will scroll (list/collection) to that indexPath, and try to capture the cell view.
|
||||||
|
2. After cell is identified then we are identifying Carousel from the view hierarchy & scroll to the Carousel item which matches the carouselItemModel.id
|
||||||
|
3. After carouselItemModel is scrolled then traversing the carouselCellItem hierarchy which matches the trait & id of that view's model
|
||||||
|
4. After identifying the element, then will post the accessibility notification directly to that UI element
|
||||||
|
*/
|
||||||
|
private func createRotor(for type: RotorType) -> UIAccessibilityCustomRotor? {
|
||||||
|
return type.getUIAccessibilityCustomRotor { [weak self] predicate in
|
||||||
|
guard let self, let elements = self.rotorElements[type] else { return UIAccessibilityCustomRotorItemResult() }
|
||||||
|
var rotorIndex = self.rotorIndexes[type] ?? -1
|
||||||
|
switch predicate.searchDirection {
|
||||||
|
case .next:
|
||||||
|
if rotorIndex + 1 < elements.count {
|
||||||
|
rotorIndex += 1
|
||||||
|
}
|
||||||
|
case .previous:
|
||||||
|
if rotorIndex > 0 {
|
||||||
|
rotorIndex -= 1
|
||||||
|
}
|
||||||
|
@unknown default:
|
||||||
|
rotorIndex = 0
|
||||||
|
}
|
||||||
|
guard rotorIndex >= 0, !elements.isEmpty else { return UIAccessibilityCustomRotorItemResult() } //Safety check to avoid crash.
|
||||||
|
var rotorElement = elements[rotorIndex]
|
||||||
|
if let element = rotorElement as? RotorElement,
|
||||||
|
let controller = self.delegate as? (RotorViewElementsProtocol & ViewController) {
|
||||||
|
var cellView: UIView?
|
||||||
|
if let indexPath = element.indexPath, let controller = self.delegate as? (any RotorListTypeDelegateProtocol) {
|
||||||
|
controller.scrollToRow(at: indexPath, animated: false)
|
||||||
|
cellView = controller.cellForRow(at: indexPath)
|
||||||
|
} else {
|
||||||
|
cellView = controller.view
|
||||||
|
}
|
||||||
|
if let cauroselItemModel = element.carouselItemModel {
|
||||||
|
guard let carousel = MVMCoreUIUtility.findViews(by: Carousel.self, views: [cellView].compactMap { $0 }).first,
|
||||||
|
let index = (carousel.molecules?.firstIndex { $0.id == cauroselItemModel.id }) else { return UIAccessibilityCustomRotorItemResult() }
|
||||||
|
let collectionIndexPath = IndexPath(item: index, section: 0)
|
||||||
|
carousel.collectionView.scrollToItem(at: collectionIndexPath, at: .left, animated: false)
|
||||||
|
let collectionViewCell = carousel.collectionView.cellForItem(at: collectionIndexPath)
|
||||||
|
rotorElement = MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [collectionViewCell].compactMap { $0 }).filter { type.filter($0) && $0.model?.id == element.model.id }.first as Any
|
||||||
|
} else {
|
||||||
|
rotorElement = MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [cellView].compactMap { $0 }).filter { type.filter($0) && $0.model?.id == element.model.id }.first as Any
|
||||||
|
if let viewElement = (rotorElement as? UIView) {
|
||||||
|
let convertedFrame = viewElement.convert(viewElement.frame, to: controller.view)
|
||||||
|
(self.delegate as? RotorScrollDelegateProtocol)?.scrollRectToVisible(convertedFrame, animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let viewElement = (rotorElement as? UIView) {
|
||||||
|
let convertedFrame = viewElement.convert(viewElement.frame, to: (self.delegate as? UIViewController)?.view)
|
||||||
|
(self.delegate as? RotorScrollDelegateProtocol)?.scrollRectToVisible(convertedFrame, animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.rotorIndexes[type] = rotorIndex
|
||||||
|
self.accessibilityHandler?.post(notification: .layoutChanged, argument: rotorElement)
|
||||||
|
return UIAccessibilityCustomRotorItemResult(targetElement: rotorElement as! NSObjectProtocol, targetRange: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - Protocol Extensions
|
||||||
|
public extension RotorViewElementsProtocol {
|
||||||
|
var topView: UIView? { nil }
|
||||||
|
var middleView: UIView? { nil }
|
||||||
|
var bottomView: UIView? { nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RotorScrollDelegateProtocol {
|
||||||
|
public func scrollRectToVisible(_ rect: CGRect, animated: Bool) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RotorListTypeDelegateProtocol {
|
||||||
|
|
||||||
|
public func scrollToRow(at indexPath: IndexPath, animated: Bool) { }
|
||||||
|
public func cellForRow(at indexPath: IndexPath) -> UIView? { nil }
|
||||||
|
public func getIndexPathFor(molecule: MoleculeModelProtocol) -> IndexPath? { nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ThreeLayerTableViewController: RotorListTypeDelegateProtocol {
|
||||||
|
|
||||||
|
public func scrollToRow(at indexPath: IndexPath, animated: Bool) {
|
||||||
|
tableView.scrollToRow(at: indexPath, at: .middle, animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cellForRow(at indexPath: IndexPath) -> UIView? {
|
||||||
|
tableView.cellForRow(at: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getIndexPathFor(molecule: MoleculeModelProtocol) -> IndexPath? {
|
||||||
|
guard let molecule = molecule as? (ListItemModelProtocol & MoleculeModelProtocol) else { return nil }
|
||||||
|
return (self as? MoleculeListTemplate)?.getIndexPath(for: molecule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ThreeLayerViewController: RotorScrollDelegateProtocol {
|
||||||
|
|
||||||
|
public func scrollRectToVisible(_ rect: CGRect, animated: Bool) {
|
||||||
|
scrollView?.scrollRectToVisible(rect, animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ThreeLayerCollectionViewController: RotorListTypeDelegateProtocol {
|
||||||
|
|
||||||
|
public func scrollToRow(at indexPath: IndexPath, animated: Bool) {
|
||||||
|
collectionView?.scrollToItem(at: indexPath, at: .centeredVertically, animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cellForRow(at indexPath: IndexPath) -> UIView? {
|
||||||
|
collectionView?.cellForItem(at: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getIndexPathFor(molecule: MoleculeModelProtocol) -> IndexPath? {
|
||||||
|
guard let molecule = molecule as? (CollectionItemModelProtocol & MoleculeModelProtocol) else { return nil }
|
||||||
|
return (self as? MoleculeCollectionListProtocol)?.getIndexPath(for: molecule)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,10 +8,11 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
import VDS
|
||||||
|
import MVMCore
|
||||||
|
|
||||||
public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?)
|
public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?)
|
||||||
|
|
||||||
|
|
||||||
open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
|
open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
@ -26,56 +27,14 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
public var action: ActionModelProtocol
|
public var action: ActionModelProtocol
|
||||||
public var enabled: Bool = true
|
public var enabled: Bool = true
|
||||||
public var width: CGFloat?
|
public var width: CGFloat?
|
||||||
public var style: Styler.Button.Style? {
|
public var style: Use = .primary
|
||||||
didSet {
|
public var size: VDS.Button.Size = .large
|
||||||
guard let style = style else { return }
|
|
||||||
setFacade(by: style)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public var size: Styler.Button.Size? = .standard
|
|
||||||
public var groupName: String = ""
|
public var groupName: String = ""
|
||||||
public var inverted: Bool = false
|
public var inverted: Bool = false
|
||||||
|
|
||||||
public lazy var enabledColors: FacadeElements = (fill: enabled_fillColor(),
|
|
||||||
text: enabled_textColor(),
|
|
||||||
border: enabled_borderColor())
|
|
||||||
|
|
||||||
public lazy var disabledColors: FacadeElements = (fill: disabled_fillColor(),
|
|
||||||
text: disabled_textColor(),
|
|
||||||
border: disabled_borderColor())
|
|
||||||
|
|
||||||
public var enabledFillColor: Color?
|
|
||||||
public var enabledTextColor: Color?
|
|
||||||
public var enabledBorderColor: Color?
|
|
||||||
|
|
||||||
public var enabledFillColor_inverted: Color?
|
|
||||||
public var enabledTextColor_inverted: Color?
|
|
||||||
public var enabledBorderColor_inverted: Color?
|
|
||||||
|
|
||||||
public var disabledFillColor: Color?
|
|
||||||
public var disabledTextColor: Color?
|
|
||||||
public var disabledBorderColor: Color?
|
|
||||||
|
|
||||||
public var disabledFillColor_inverted: Color?
|
|
||||||
public var disabledTextColor_inverted: Color?
|
|
||||||
public var disabledBorderColor_inverted: Color?
|
|
||||||
|
|
||||||
private var _backgroundColor: Color?
|
|
||||||
public var backgroundColor: Color? {
|
|
||||||
get {
|
|
||||||
if let backgroundColor = _backgroundColor { return backgroundColor }
|
|
||||||
if inverted {
|
|
||||||
return enabled ? enabledFillColor_inverted : disabledFillColor_inverted
|
|
||||||
}
|
|
||||||
return enabled ? enabledFillColor : disabledFillColor
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_backgroundColor = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public var accessibilityTraits: UIAccessibilityTraits?
|
public var accessibilityTraits: UIAccessibilityTraits?
|
||||||
public var disabledAccessibilityTraits: UIAccessibilityTraits?
|
public var disabledAccessibilityTraits: UIAccessibilityTraits?
|
||||||
|
public var backgroundColor: Color?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -89,88 +48,18 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
public init(with title: String, action: ActionModelProtocol) {
|
public init(with title: String, action: ActionModelProtocol) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.action = action
|
self.action = action
|
||||||
setFacade(by: .primary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(secondaryButtonWith title: String, action: ActionModelProtocol) {
|
public init(secondaryButtonWith title: String, action: ActionModelProtocol) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.action = action
|
self.action = action
|
||||||
style = .secondary
|
style = .secondary
|
||||||
setFacade(by: .secondary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(primaryButtonWith title: String, action: ActionModelProtocol) {
|
public init(primaryButtonWith title: String, action: ActionModelProtocol) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.action = action
|
self.action = action
|
||||||
style = .primary
|
style = .primary
|
||||||
setFacade(by: .primary)
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Methods
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
public func enabled_fillColor() -> UIColor? {
|
|
||||||
(inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor
|
|
||||||
}
|
|
||||||
|
|
||||||
public func enabled_textColor() -> UIColor? {
|
|
||||||
(inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor
|
|
||||||
}
|
|
||||||
|
|
||||||
public func enabled_borderColor() -> UIColor? {
|
|
||||||
(inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor
|
|
||||||
}
|
|
||||||
|
|
||||||
public func disabled_fillColor() -> UIColor? {
|
|
||||||
(inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor
|
|
||||||
}
|
|
||||||
|
|
||||||
public func disabled_textColor() -> UIColor? {
|
|
||||||
(inverted ? disabledTextColor_inverted : disabledTextColor)?.uiColor
|
|
||||||
}
|
|
||||||
|
|
||||||
public func disabled_borderColor() -> UIColor? {
|
|
||||||
(inverted ? disabledBorderColor_inverted : disabledBorderColor)?.uiColor
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the default appearance for the primary style.
|
|
||||||
func setPrimaryFacade() {
|
|
||||||
enabledFillColor = Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
|
||||||
enabledTextColor = Color(uiColor: VDSColor.elementsPrimaryOndark)
|
|
||||||
disabledFillColor = Color(uiColor: VDSColor.interactiveDisabledOnlight)
|
|
||||||
disabledTextColor = Color(uiColor: VDSColor.elementsPrimaryOndark)
|
|
||||||
|
|
||||||
enabledFillColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark)
|
|
||||||
enabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
|
||||||
disabledFillColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
|
|
||||||
disabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the default appearance for the Secondary style.
|
|
||||||
func setSecondaryFacade() {
|
|
||||||
enabledTextColor = Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
|
||||||
enabledFillColor = Color(uiColor: UIColor.clear)
|
|
||||||
enabledBorderColor = Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
|
||||||
disabledTextColor = Color(uiColor: VDSColor.interactiveDisabledOnlight)
|
|
||||||
disabledBorderColor = Color(uiColor: VDSColor.interactiveDisabledOnlight)
|
|
||||||
|
|
||||||
enabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark)
|
|
||||||
enabledFillColor_inverted = Color(uiColor: UIColor.clear)
|
|
||||||
enabledBorderColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark)
|
|
||||||
disabledTextColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
|
|
||||||
disabledBorderColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func setFacade(by style: Styler.Button.Style) {
|
|
||||||
|
|
||||||
switch style {
|
|
||||||
case .primary:
|
|
||||||
setPrimaryFacade()
|
|
||||||
|
|
||||||
case .secondary:
|
|
||||||
setSecondaryFacade()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -180,7 +69,6 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case id
|
case id
|
||||||
case moleculeName
|
case moleculeName
|
||||||
case backgroundColor
|
|
||||||
case accessibilityIdentifier
|
case accessibilityIdentifier
|
||||||
case accessibilityText
|
case accessibilityText
|
||||||
case title
|
case title
|
||||||
@ -190,12 +78,6 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
case style
|
case style
|
||||||
case size
|
case size
|
||||||
case groupName
|
case groupName
|
||||||
case fillColor
|
|
||||||
case textColor
|
|
||||||
case borderColor
|
|
||||||
case disabledFillColor
|
|
||||||
case disabledTextColor
|
|
||||||
case disabledBorderColor
|
|
||||||
case width
|
case width
|
||||||
case accessibilityTraits
|
case accessibilityTraits
|
||||||
case disabledAccessibilityTraits
|
case disabledAccessibilityTraits
|
||||||
@ -215,17 +97,13 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
action = try typeContainer.decodeModel(codingKey: .action)
|
action = try typeContainer.decodeModel(codingKey: .action)
|
||||||
|
|
||||||
///Style captured from the JSON
|
///Style captured from the JSON
|
||||||
if let style = try typeContainer.decodeIfPresent(Styler.Button.Style.self, forKey: .style){
|
if let style = try typeContainer.decodeIfPresent(Use.self, forKey: .style) {
|
||||||
self.style = style
|
self.style = style
|
||||||
setFacade(by: style)
|
} else if let style = decoder.context?.value(forKey: CodingKeys.style.stringValue) as? Use { ///Reading the style param from context which is set is molecules, ex: TwoButtonView
|
||||||
} else if let style = decoder.context?.value(forKey: CodingKeys.style.stringValue) as? Styler.Button.Style { ///Reading the style param from context which is set is molecules, ex: TwoButtonView
|
|
||||||
self.style = style
|
self.style = style
|
||||||
setFacade(by: style)
|
|
||||||
} else { ///Default style
|
|
||||||
setFacade(by: .primary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let size = try typeContainer.decodeIfPresent(Styler.Button.Size.self, forKey: .size) {
|
if let size = try typeContainer.decodeIfPresent(VDS.Button.Size.self, forKey: .size) {
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,42 +111,62 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) {
|
|
||||||
self.inverted = inverted
|
|
||||||
}
|
|
||||||
|
|
||||||
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
|
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
|
||||||
self.groupName = groupName
|
self.groupName = groupName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
width = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .width)
|
||||||
|
|
||||||
|
if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) {
|
||||||
|
self.inverted = inverted
|
||||||
|
} else {
|
||||||
|
try setInverted(deprecatedFrom: decoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
accessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .accessibilityTraits)
|
||||||
|
|
||||||
|
disabledAccessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .disabledAccessibilityTraits)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DeprecatedCodingKeys: String, CodingKey {
|
||||||
|
case fillColor
|
||||||
|
case textColor
|
||||||
|
case borderColor
|
||||||
|
case backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setInverted(deprecatedFrom decoder: Decoder) throws {
|
||||||
|
let typeContainer = try decoder.container(keyedBy: DeprecatedCodingKeys.self)
|
||||||
if let enabledFillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor) {
|
if let enabledFillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor) {
|
||||||
self.enabledFillColor = enabledFillColor
|
if (self.style == .secondary) {
|
||||||
|
self.inverted = enabledFillColor.uiColor.isDark()
|
||||||
|
} else {
|
||||||
|
self.inverted = !enabledFillColor.uiColor.isDark()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) {
|
if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) {
|
||||||
self.enabledTextColor = enabledTextColor
|
if (self.style == .secondary) {
|
||||||
|
self.inverted = !enabledTextColor.uiColor.isDark()
|
||||||
|
} else {
|
||||||
|
self.inverted = enabledTextColor.uiColor.isDark()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let enabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) {
|
if let enabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) {
|
||||||
self.enabledBorderColor = enabledBorderColor
|
if (self.style == .secondary) {
|
||||||
|
self.inverted = !enabledBorderColor.uiColor.isDark()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) {
|
||||||
|
if (self.style == .secondary) {
|
||||||
|
self.inverted = backgroundColor.uiColor.isDark()
|
||||||
|
} else {
|
||||||
|
self.inverted = !backgroundColor.uiColor.isDark()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let disabledFillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledFillColor) {
|
|
||||||
self.disabledFillColor = disabledFillColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) {
|
|
||||||
self.disabledTextColor = disabledTextColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) {
|
|
||||||
self.disabledBorderColor = disabledBorderColor
|
|
||||||
}
|
|
||||||
|
|
||||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
|
||||||
width = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .width)
|
|
||||||
accessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .accessibilityTraits)
|
|
||||||
disabledAccessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .disabledAccessibilityTraits)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open func encode(to encoder: Encoder) throws {
|
open func encode(to encoder: Encoder) throws {
|
||||||
@ -279,17 +177,10 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
try container.encode(enabled, forKey: .enabled)
|
try container.encode(enabled, forKey: .enabled)
|
||||||
try container.encode(inverted, forKey: .inverted)
|
try container.encode(inverted, forKey: .inverted)
|
||||||
try container.encodeModel(action, forKey: .action)
|
try container.encodeModel(action, forKey: .action)
|
||||||
try container.encodeIfPresent(_backgroundColor, forKey: .backgroundColor)
|
|
||||||
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
|
||||||
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
|
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
|
||||||
try container.encodeIfPresent(enabledFillColor, forKey: .fillColor)
|
try container.encode(style, forKey: .style)
|
||||||
try container.encodeIfPresent(enabledTextColor, forKey: .textColor)
|
try container.encode(size, forKey: .size)
|
||||||
try container.encodeIfPresent(enabledBorderColor, forKey: .borderColor)
|
|
||||||
try container.encodeIfPresent(disabledFillColor, forKey: .disabledFillColor)
|
|
||||||
try container.encodeIfPresent(disabledTextColor, forKey: .disabledTextColor)
|
|
||||||
try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor)
|
|
||||||
try container.encodeIfPresent(style, forKey: .style)
|
|
||||||
try container.encodeIfPresent(size, forKey: .size)
|
|
||||||
try container.encodeIfPresent(groupName, forKey: .groupName)
|
try container.encodeIfPresent(groupName, forKey: .groupName)
|
||||||
try container.encodeIfPresent(width, forKey: .width)
|
try container.encodeIfPresent(width, forKey: .width)
|
||||||
try container.encodeIfPresent(accessibilityTraits, forKey: .accessibilityTraits)
|
try container.encodeIfPresent(accessibilityTraits, forKey: .accessibilityTraits)
|
||||||
|
|||||||
@ -11,30 +11,26 @@ import UIKit
|
|||||||
|
|
||||||
open class ExternalLink: Link {
|
open class ExternalLink: Link {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
open var exportImageView: UIImageView?
|
||||||
public var exportImageView: UIImageView?
|
open var exportImageHeight: NSLayoutConstraint?
|
||||||
|
open var exportImageWidth: NSLayoutConstraint?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - MoleculeViewProtocol
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
open override func viewModelDidUpdate() {
|
||||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
super.viewModelDidUpdate()
|
||||||
super.set(with: model, delegateObject, additionalData)
|
exportImageView?.tintColor = textColor
|
||||||
|
exportImageWidth?.constant = textStyle.lineHeight
|
||||||
guard let model = model as? ExternalLinkModel else { return }
|
exportImageHeight?.constant = textStyle.lineHeight
|
||||||
|
|
||||||
exportImageView?.tintColor = titleColor(for: model.enabled ? .normal : .disabled)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - MVMCoreViewProtocol
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
open override func setupView() {
|
|
||||||
super.setupView()
|
|
||||||
|
|
||||||
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
|
contentHorizontalAlignment = .left
|
||||||
|
|
||||||
let image = MVMCoreUIUtility.imageNamed("externalLink")
|
let image = MVMCoreUIUtility.imageNamed("externalLink")
|
||||||
exportImageView = UIImageView(image: image?.withRenderingMode(.alwaysTemplate))
|
exportImageView = UIImageView(image: image?.withRenderingMode(.alwaysTemplate))
|
||||||
|
|
||||||
@ -46,10 +42,10 @@ open class ExternalLink: Link {
|
|||||||
addSubview(exportIcon)
|
addSubview(exportIcon)
|
||||||
trailingAnchor.constraint(greaterThanOrEqualTo: exportIcon.trailingAnchor).isActive = true
|
trailingAnchor.constraint(greaterThanOrEqualTo: exportIcon.trailingAnchor).isActive = true
|
||||||
|
|
||||||
if let titleLabel = titleLabel {
|
exportImageHeight = exportIcon.heightAnchor.constraint(equalToConstant: textStyle.pointSize).activate()
|
||||||
let dimension = titleLabel.font.pointSize
|
exportImageWidth = exportIcon.widthAnchor.constraint(equalToConstant: textStyle.pointSize).activate()
|
||||||
exportIcon.heightAnchor.constraint(equalToConstant: dimension).isActive = true
|
|
||||||
exportIcon.widthAnchor.constraint(equalToConstant: dimension).isActive = true
|
if let titleLabel {
|
||||||
exportIcon.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 4).isActive = true
|
exportIcon.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 4).isActive = true
|
||||||
exportIcon.bottomAnchor.constraint(equalTo: titleLabel.lastBaselineAnchor, constant: 3).isActive = true
|
exportIcon.bottomAnchor.constraint(equalTo: titleLabel.lastBaselineAnchor, constant: 3).isActive = true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,83 +8,59 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
import VDS
|
||||||
|
|
||||||
|
open class Link: VDS.TextLink, VDSMoleculeViewProtocol {
|
||||||
|
|
||||||
@objcMembers open class Link: Button {
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Draw
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
open var viewModel: LinkModel!
|
||||||
|
open var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
open var additionalData: [AnyHashable : Any]?
|
||||||
|
|
||||||
open override func draw(_ rect: CGRect) {
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Functions
|
||||||
|
//--------------------------------------------------
|
||||||
|
open func viewModelDidUpdate() {
|
||||||
|
isEnabled = viewModel.enabled
|
||||||
|
size = viewModel.size
|
||||||
|
text = viewModel.title
|
||||||
|
surface = viewModel.surface
|
||||||
|
|
||||||
guard let textRect = titleLabel?.frame,
|
onClick = { [weak self] control in
|
||||||
let context = UIGraphicsGetCurrentContext()
|
guard let self else { return }
|
||||||
else { return }
|
MVMCoreUIActionHandler.performActionUnstructured(with: self.viewModel.action,
|
||||||
|
sourceModel: self.viewModel,
|
||||||
|
additionalData: self.additionalData,
|
||||||
|
delegateObject: self.delegateObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Overrides
|
||||||
|
//--------------------------------------------------
|
||||||
|
open override func updateAccessibility() {
|
||||||
|
super.updateAccessibility()
|
||||||
|
|
||||||
// Set line to the same color as the text
|
guard let viewModel = viewModel else { return }
|
||||||
if let color = titleColor(for: state)?.cgColor {
|
if let accessibilityText = viewModel.accessibilityText {
|
||||||
context.setStrokeColor(color)
|
self.accessibilityLabel = accessibilityText
|
||||||
}
|
}
|
||||||
|
|
||||||
// x should be according to the text, not the button
|
if let accessibilityIdentifier = viewModel.accessibilityIdentifier {
|
||||||
let x = textRect.origin.x
|
self.accessibilityIdentifier = accessibilityIdentifier
|
||||||
|
|
||||||
// Line is 0 point below the text
|
|
||||||
let y = textRect.origin.y + textRect.size.height
|
|
||||||
|
|
||||||
context.move(to: CGPoint(x: x, y: y))
|
|
||||||
context.addLine(to: CGPoint(x: x + textRect.size.width, y: y))
|
|
||||||
context.strokePath()
|
|
||||||
}
|
|
||||||
|
|
||||||
open override var intrinsicContentSize: CGSize {
|
|
||||||
guard let size = titleLabel?.intrinsicContentSize else { return super.intrinsicContentSize }
|
|
||||||
return CGSize(width: size.width, height: size.height + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - MoleculeViewProtocol
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
|
||||||
super.set(with: model, delegateObject, additionalData)
|
|
||||||
|
|
||||||
guard let model = model as? LinkModel else { return }
|
|
||||||
|
|
||||||
setTitle(model.title, for: .normal)
|
|
||||||
if let accessibilityText = model.accessibilityText {
|
|
||||||
accessibilityLabel = accessibilityText
|
|
||||||
}
|
}
|
||||||
setTitleColor((model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor, for: .normal)
|
|
||||||
setTitleColor((model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor, for: .disabled)
|
|
||||||
setTitleColor((model.inverted ? model.activeColor_inverted : model.activeColor).uiColor, for: .highlighted)
|
|
||||||
isEnabled = model.enabled
|
|
||||||
titleLabel?.font = model.getFont(model.size)
|
|
||||||
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 31 }
|
//--------------------------------------------------
|
||||||
}
|
// MARK: - MVMCoreViewProtocol
|
||||||
|
//--------------------------------------------------
|
||||||
|
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 31 }
|
||||||
|
|
||||||
// MARK: - MVMCoreViewProtocol
|
open func updateView(_ size: CGFloat) { }
|
||||||
extension Link {
|
|
||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open func setupView() {}
|
||||||
super.updateView(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func setupView() {
|
|
||||||
super.setupView()
|
|
||||||
backgroundColor = .clear
|
|
||||||
contentMode = .redraw
|
|
||||||
setTitleColor(VDSColor.elementsPrimaryOnlight, for: .normal)
|
|
||||||
setTitleColor(VDSColor.interactiveDisabledOnlight, for: .disabled)
|
|
||||||
setTitleColor(VDSColor.interactiveActiveOnlight, for: .highlighted)
|
|
||||||
titleLabel?.numberOfLines = 1
|
|
||||||
titleLabel?.lineBreakMode = .byTruncatingTail
|
|
||||||
titleLabel?.textAlignment = .left
|
|
||||||
contentHorizontalAlignment = .left
|
|
||||||
contentVerticalAlignment = .top
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MVMCoreUIViewConstrainingProtocol
|
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDS
|
||||||
|
|
||||||
open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableModelProtocol {
|
open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableModelProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -23,15 +23,9 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
|
|||||||
public var accessibilityText: String?
|
public var accessibilityText: String?
|
||||||
public var action: ActionModelProtocol
|
public var action: ActionModelProtocol
|
||||||
public var enabled = true
|
public var enabled = true
|
||||||
public var enabledColor = Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
|
||||||
public var enabledColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark)
|
|
||||||
public var disabledColor = Color(uiColor: VDSColor.interactiveDisabledOnlight)
|
|
||||||
public var disabledColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
|
|
||||||
public var activeColor = Color(uiColor: VDSColor.interactiveActiveOnlight)
|
|
||||||
public var activeColor_inverted = Color(uiColor: VDSColor.interactiveActiveOndark)
|
|
||||||
|
|
||||||
public var inverted = false
|
public var inverted = false
|
||||||
public var size:linkFontSize = linkFontSize.small
|
public var size: TextLink.Size = .small
|
||||||
|
|
||||||
public var shouldMaskRecordedView: Bool? = false
|
public var shouldMaskRecordedView: Bool? = false
|
||||||
|
|
||||||
@ -57,34 +51,10 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
|
|||||||
case title
|
case title
|
||||||
case action
|
case action
|
||||||
case enabled
|
case enabled
|
||||||
case enabledColor
|
|
||||||
case enabledColor_inverted
|
|
||||||
case disabledColor
|
|
||||||
case disabledColor_inverted
|
|
||||||
case activeColor
|
|
||||||
case activeColor_inverted
|
|
||||||
case inverted
|
case inverted
|
||||||
case size
|
case size
|
||||||
case shouldMaskRecordedView
|
case shouldMaskRecordedView
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum linkFontSize: String, Codable {
|
|
||||||
case small
|
|
||||||
case large
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Method
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
func getFont(_ type: linkFontSize) -> UIFont {
|
|
||||||
switch type {
|
|
||||||
case .small:
|
|
||||||
return MFStyler.fontRegularBodySmall()
|
|
||||||
case .large:
|
|
||||||
return MFStyler.fontRegularBodyLarge()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Codec
|
// MARK: - Codec
|
||||||
@ -108,30 +78,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
|
|||||||
self.inverted = inverted
|
self.inverted = inverted
|
||||||
}
|
}
|
||||||
|
|
||||||
if let enabledColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor) {
|
if let size = try typeContainer.decodeIfPresent(TextLink.Size.self, forKey: .size) {
|
||||||
self.enabledColor = enabledColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let enabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor_inverted) {
|
|
||||||
self.enabledColor_inverted = enabledColor_inverted
|
|
||||||
}
|
|
||||||
|
|
||||||
if let disabledColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor) {
|
|
||||||
self.disabledColor = disabledColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let disabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor_inverted) {
|
|
||||||
self.disabledColor_inverted = disabledColor_inverted
|
|
||||||
}
|
|
||||||
|
|
||||||
if let activeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .activeColor) {
|
|
||||||
self.activeColor = activeColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let activeColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .activeColor_inverted) {
|
|
||||||
self.activeColor_inverted = activeColor_inverted
|
|
||||||
}
|
|
||||||
if let size = try typeContainer.decodeIfPresent(linkFontSize.self, forKey: .size) {
|
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,13 +95,11 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
|
|||||||
try container.encodeModel(action, forKey: .action)
|
try container.encodeModel(action, forKey: .action)
|
||||||
try container.encode(inverted, forKey: .inverted)
|
try container.encode(inverted, forKey: .inverted)
|
||||||
try container.encode(enabled, forKey: .enabled)
|
try container.encode(enabled, forKey: .enabled)
|
||||||
try container.encode(enabledColor, forKey: .enabledColor)
|
|
||||||
try container.encode(enabledColor_inverted, forKey: .enabledColor_inverted)
|
|
||||||
try container.encode(disabledColor, forKey: .disabledColor)
|
|
||||||
try container.encode(disabledColor_inverted, forKey: .disabledColor_inverted)
|
|
||||||
try container.encode(activeColor, forKey: .activeColor)
|
|
||||||
try container.encode(activeColor_inverted, forKey: .activeColor_inverted)
|
|
||||||
try container.encodeIfPresent(size, forKey: .size)
|
try container.encodeIfPresent(size, forKey: .size)
|
||||||
try container.encode(shouldMaskRecordedView, forKey: .shouldMaskRecordedView)
|
try container.encode(shouldMaskRecordedView, forKey: .shouldMaskRecordedView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension LinkModel {
|
||||||
|
public var surface: Surface { inverted ? .dark : .light }
|
||||||
|
}
|
||||||
|
|||||||
@ -8,226 +8,72 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
import VDS
|
||||||
|
import MVMCore
|
||||||
|
import Combine
|
||||||
|
|
||||||
open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
open class PillButton: VDS.Button, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, VDSMoleculeViewProtocol {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
/// Used to size the button.
|
open var viewModel: ButtonModel!
|
||||||
var size = MVMCoreUIUtility.getWidth()
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
public var additionalData: [AnyHashable: Any]?
|
||||||
var buttonModel: ButtonModel? {
|
|
||||||
get { model as? ButtonModel }
|
internal var onClickCancellable: Cancellable?
|
||||||
}
|
|
||||||
|
|
||||||
/// Need to re-style on set.
|
|
||||||
open override var isEnabled: Bool {
|
|
||||||
didSet { style(with: buttonModel) }
|
|
||||||
}
|
|
||||||
|
|
||||||
open var buttonSize: Styler.Button.Size = .standard {
|
|
||||||
didSet { buttonModel?.size = buttonSize }
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Constraints
|
// MARK: - VDSMoleculeViewProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var widthConstraint: NSLayoutConstraint?
|
open func viewModelDidUpdate() {
|
||||||
public var minimumWidthConstraint: NSLayoutConstraint?
|
if let accessibilityIdentifier = viewModel.accessibilityIdentifier {
|
||||||
|
self.accessibilityIdentifier = accessibilityIdentifier
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Initializers
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
@objc public convenience init(asPrimaryButton isPrimary: Bool, makeTiny istiny: Bool) {
|
|
||||||
let model = ButtonModel(with: "", action: ActionNoopModel())
|
|
||||||
model.style = isPrimary ? .primary : .secondary
|
|
||||||
model.size = istiny ? .tiny : .standard
|
|
||||||
self.init(model: model, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Computed Properties
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
public var enabledTitleColor: UIColor? {
|
|
||||||
get { titleColor(for: .normal) }
|
|
||||||
set { setTitleColor(newValue, for: .normal) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public var disabledTitleColor: UIColor? {
|
|
||||||
get { titleColor(for: .disabled) }
|
|
||||||
set { setTitleColor(newValue, for: .disabled) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public var borderColor: UIColor? {
|
|
||||||
get {
|
|
||||||
guard let currentColor = layer.borderColor else { return nil }
|
|
||||||
return UIColor(cgColor: currentColor)
|
|
||||||
}
|
|
||||||
set { layer.borderColor = newValue?.cgColor }
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Methods
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
/// The primary styling for a button. Should be used for main buttons
|
|
||||||
public func stylePrimary() {
|
|
||||||
let buttonModel = ButtonModel(primaryButtonWith: "", action: ActionNoopModel())
|
|
||||||
style(with: buttonModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The secondary styling for a button. Should be used for secondary buttons
|
|
||||||
public func styleSecondary() {
|
|
||||||
let buttonModel = ButtonModel(secondaryButtonWith: "", action: ActionNoopModel())
|
|
||||||
style(with: buttonModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Styles the button based on the model style
|
|
||||||
private func style(with model: ButtonModel?) {
|
|
||||||
|
|
||||||
layer.borderWidth = model?.style == .secondary ? 1 : 0
|
|
||||||
|
|
||||||
if let titleColor = model?.enabledColors.text {
|
|
||||||
enabledTitleColor = titleColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let disabledTitleColor = model?.disabledColors.text {
|
text = viewModel.title
|
||||||
self.disabledTitleColor = disabledTitleColor
|
isEnabled = viewModel.enabled
|
||||||
|
size = viewModel.size
|
||||||
|
use = viewModel.style ?? .primary
|
||||||
|
surface = viewModel.inverted ? .dark : .light
|
||||||
|
if let accessibilityText = viewModel.accessibilityText {
|
||||||
|
accessibilityLabel = accessibilityText
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
accessibilityTraits = isEnabled ? (viewModel?.accessibilityTraits ?? .button) : (viewModel?.disabledAccessibilityTraits ?? .none)
|
||||||
// Useful to detect with isHittable when performing UI testing.
|
|
||||||
isAccessibilityElement = isEnabled
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if isEnabled {
|
|
||||||
if let fillColor = model?.enabledColors.fill {
|
|
||||||
backgroundColor = fillColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let borderColor = model?.enabledColors.border {
|
|
||||||
self.borderColor = borderColor
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let fillColor = model?.disabledColors.fill {
|
|
||||||
backgroundColor = fillColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let borderColor = model?.disabledColors.border {
|
|
||||||
self.borderColor = borderColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accessibilityTraits = isEnabled ? (model?.accessibilityTraits ?? .button) : (model?.disabledAccessibilityTraits ?? .none)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getInnerPadding() -> CGFloat {
|
set(with: viewModel.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||||
buttonSize.getHeight() / 2.0
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getContentEdgeInsets() -> UIEdgeInsets {
|
viewModel.updateUI = { [weak self] in
|
||||||
var verticalPadding = 0.0
|
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
||||||
var horizontalPadding = 0.0
|
guard let self = self else { return }
|
||||||
switch buttonSize {
|
self.isEnabled = self.viewModel.enabled
|
||||||
case .standard:
|
})
|
||||||
verticalPadding = Padding.Three
|
|
||||||
horizontalPadding = Padding.Five
|
|
||||||
|
|
||||||
break
|
|
||||||
case .small:
|
|
||||||
verticalPadding = Padding.Two
|
|
||||||
horizontalPadding = Padding.Four
|
|
||||||
break
|
|
||||||
case .tiny:
|
|
||||||
verticalPadding = Padding.One
|
|
||||||
horizontalPadding = Padding.Two
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
return UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
|
|
||||||
|
FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - MVMCoreViewProtocol
|
// MARK: - MVMCoreViewProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
|
||||||
// The button will get styled in the enable check in super.
|
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||||
super.set(with: model, delegateObject, additionalData)
|
return (model as? ButtonModel)?.size.height
|
||||||
|
|
||||||
guard let model = model as? ButtonModel else { return }
|
|
||||||
|
|
||||||
setTitle(model.title, for: .normal)
|
|
||||||
if let accessibilityText = model.accessibilityText {
|
|
||||||
accessibilityLabel = accessibilityText
|
|
||||||
}
|
|
||||||
|
|
||||||
if let size = model.size {
|
|
||||||
buttonSize = size
|
|
||||||
}
|
|
||||||
|
|
||||||
model.updateUI = { [weak self] in
|
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
|
||||||
self?.enableField(model.enabled)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
open func updateView(_ size: CGFloat) {}
|
||||||
return (model as? ButtonModel)?.size?.getHeight()
|
|
||||||
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
|
setupView()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open func setupView() {}
|
||||||
super.updateView(size)
|
|
||||||
self.size = size
|
|
||||||
|
|
||||||
switch buttonSize {
|
|
||||||
case .tiny:
|
|
||||||
titleLabel?.font = Styler.Font.BoldMicro.getFont(false)
|
|
||||||
case .small:
|
|
||||||
titleLabel?.font = Styler.Font.BoldBodySmall.getFont(false)
|
|
||||||
case .standard:
|
|
||||||
titleLabel?.font = Styler.Font.BoldBodyLarge.getFont(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.cornerRadius = getInnerPadding()
|
|
||||||
contentEdgeInsets = getContentEdgeInsets()
|
|
||||||
|
|
||||||
if let contraint = buttonModel?.width {
|
|
||||||
|
|
||||||
if widthConstraint == nil {
|
|
||||||
widthConstraint = widthAnchor.constraint(equalToConstant: contraint)
|
|
||||||
} else if widthConstraint?.constant != contraint {
|
|
||||||
widthConstraint?.constant = contraint
|
|
||||||
}
|
|
||||||
widthConstraint?.isActive = true
|
|
||||||
minimumWidthConstraint?.isActive = false
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if minimumWidthConstraint == nil {
|
|
||||||
minimumWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: buttonSize.minimumWidth())
|
|
||||||
} else {
|
|
||||||
minimumWidthConstraint?.constant = buttonSize.minimumWidth()
|
|
||||||
}
|
|
||||||
minimumWidthConstraint?.isActive = true
|
|
||||||
widthConstraint?.isActive = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func setupView() {
|
|
||||||
super.setupView()
|
|
||||||
|
|
||||||
titleLabel?.numberOfLines = 1
|
|
||||||
titleLabel?.lineBreakMode = .byTruncatingTail
|
|
||||||
titleLabel?.textAlignment = .center
|
|
||||||
contentHorizontalAlignment = .center
|
|
||||||
stylePrimary()
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - MVMCoreUIViewConstrainingProtocol
|
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||||
@ -235,7 +81,21 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
|||||||
|
|
||||||
open func horizontalAlignment() -> UIStackView.Alignment { .center }
|
open func horizontalAlignment() -> UIStackView.Alignment { .center }
|
||||||
|
|
||||||
public func enableField(_ enable: Bool) {
|
//--------------------------------------------------
|
||||||
isEnabled = enable
|
// MARK: - Action
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
open func set(with actionModel: ActionModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||||
|
onClick = { [weak self] control in
|
||||||
|
guard let self = self, let viewModel = self.viewModel else { return }
|
||||||
|
Task(priority: .userInitiated) {
|
||||||
|
try await Self.performButtonAction(with: viewModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: viewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, sourceModel: MoleculeModelProtocol? = nil) async throws {
|
||||||
|
guard delegateObject?.buttonDelegate?.button?(button, shouldPerformActionWithMap: model.toJSON(), additionalData: additionalData) ?? true else { return }
|
||||||
|
try await (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction(with: model, additionalData: MVMCoreUIActionHandler.add(sourceModel: sourceModel, to: additionalData), delegateObject: delegateObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,11 +75,8 @@ extension BaseItemPickerEntryField {
|
|||||||
@objc open override func setAccessibilityString(_ accessibilityString: String?) {
|
@objc open override func setAccessibilityString(_ accessibilityString: String?) {
|
||||||
|
|
||||||
var accessibilityString = accessibilityString ?? ""
|
var accessibilityString = accessibilityString ?? ""
|
||||||
|
textField.accessibilityTraits = .staticText
|
||||||
if let textPickerItem = MVMCoreUIUtility.hardcodedString(withKey: "textfield_picker_item") {
|
textField.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "textfield_picker_item")
|
||||||
accessibilityString += textPickerItem
|
|
||||||
}
|
|
||||||
|
|
||||||
textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")"
|
textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -379,7 +379,7 @@ import UIKit
|
|||||||
if let text = model.text, !text.isEmpty {
|
if let text = model.text, !text.isEmpty {
|
||||||
regexTextFieldOutputIfAvailable()
|
regexTextFieldOutputIfAvailable()
|
||||||
}
|
}
|
||||||
|
setAccessibilityString(model.title ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,10 +399,6 @@ extension TextEntryField {
|
|||||||
|
|
||||||
var accessibilityString = accessibilityString ?? ""
|
var accessibilityString = accessibilityString ?? ""
|
||||||
|
|
||||||
if let txtRegular = MVMCoreUIUtility.hardcodedString(withKey: "textfield_regular") {
|
|
||||||
accessibilityString += txtRegular
|
|
||||||
}
|
|
||||||
|
|
||||||
textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")"
|
textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -372,7 +372,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
|
|||||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
super.set(with: model, delegateObject, additionalData)
|
super.set(with: model, delegateObject, additionalData)
|
||||||
self.delegateObject = delegateObject
|
self.delegateObject = delegateObject
|
||||||
|
|
||||||
guard let model = model as? ToggleModel else { return }
|
guard let model = model as? ToggleModel else { return }
|
||||||
|
|
||||||
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
|
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
|
||||||
|
|||||||
@ -77,9 +77,10 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol {
|
|||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public init(_ state: Bool) {
|
public init(_ state: Bool, id: String = UUID().uuidString) {
|
||||||
self.selected = state
|
self.selected = state
|
||||||
baseValue = state
|
baseValue = state
|
||||||
|
self.id = id
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
60
MVMCoreUI/Atomic/Atoms/Views/Badge.swift
Normal file
60
MVMCoreUI/Atomic/Atoms/Views/Badge.swift
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// Badge.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Rebecca Antonelli on 4/6/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MVMCore
|
||||||
|
import VDS
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
open class Badge: VDS.Badge, VDSMoleculeViewProtocol {
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
public var viewModel: BadgeModel!
|
||||||
|
|
||||||
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
|
||||||
|
public var additionalData: [AnyHashable : Any]?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
public func viewModelDidUpdate() {
|
||||||
|
text = viewModel.text
|
||||||
|
maxWidth = viewModel.maxWidth
|
||||||
|
numberOfLines = viewModel.numberOfLines
|
||||||
|
fillColor = viewModel.fillColor
|
||||||
|
surface = viewModel.surface
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateView(_ size: CGFloat) {}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Overrides
|
||||||
|
//--------------------------------------------------
|
||||||
|
open override func updateAccessibility() {
|
||||||
|
super.updateAccessibility()
|
||||||
|
|
||||||
|
if let viewModel {
|
||||||
|
if let accessibilityText = viewModel.accessibilityText {
|
||||||
|
self.accessibilityLabel = accessibilityText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//to deal with how it's parent constrains this control
|
||||||
|
extension Badge: MVMCoreUIViewConstrainingProtocol {
|
||||||
|
|
||||||
|
public func needsToBeConstrained() -> Bool { true }
|
||||||
|
|
||||||
|
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
|
||||||
|
}
|
||||||
56
MVMCoreUI/Atomic/Atoms/Views/BadgeModel.swift
Normal file
56
MVMCoreUI/Atomic/Atoms/Views/BadgeModel.swift
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// BadgeModel.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Rebecca Antonelli on 4/6/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import VDS
|
||||||
|
|
||||||
|
open class BadgeModel: MoleculeModelProtocol {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
public static var identifier: String = "badge"
|
||||||
|
public var id: String = UUID().uuidString
|
||||||
|
public var backgroundColor: Color?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - VDS Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
public var text: String = ""
|
||||||
|
public var accessibilityText: String?
|
||||||
|
public var maxWidth: CGFloat?
|
||||||
|
public var numberOfLines: Int = 1
|
||||||
|
public var fillColor = Badge.FillColor.red
|
||||||
|
public var surface: Surface = .light
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id, text, accessibilityText, fillColor, surface, numberOfLines, maxWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
required public convenience init(from decoder: Decoder) throws {
|
||||||
|
self.init()
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
||||||
|
text = try container.decode(String.self, forKey: .text)
|
||||||
|
accessibilityText = try container.decodeIfPresent(String.self, forKey: .accessibilityText)
|
||||||
|
fillColor = try container.decodeIfPresent(Badge.FillColor.self, forKey: .fillColor) ?? .red
|
||||||
|
surface = try container.decodeIfPresent(Surface.self, forKey: .surface) ?? .light
|
||||||
|
numberOfLines = try container.decodeIfPresent(Int.self, forKey: .numberOfLines) ?? 1
|
||||||
|
maxWidth = try container.decodeIfPresent(CGFloat.self, forKey: .maxWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
try container.encode(id, forKey: .id)
|
||||||
|
try container.encode(text, forKey: .text)
|
||||||
|
try container.encode(accessibilityText, forKey: .accessibilityText)
|
||||||
|
try container.encode(fillColor, forKey: .fillColor)
|
||||||
|
try container.encode(surface, forKey: .surface)
|
||||||
|
try container.encode(numberOfLines, forKey: .numberOfLines)
|
||||||
|
try container.encodeIfPresent(maxWidth, forKey: .maxWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
public let checkbox = Checkbox()
|
public let checkbox = Checkbox()
|
||||||
public let label = Label(fontStyle: .RegularBodySmall)
|
public let label = Label(fontStyle: .RegularBodySmall)
|
||||||
|
private var observation: NSKeyValueObservation? = nil
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
@ -63,6 +64,12 @@
|
|||||||
bottomLabelConstraint.isActive = true
|
bottomLabelConstraint.isActive = true
|
||||||
|
|
||||||
alignCheckbox(.center)
|
alignCheckbox(.center)
|
||||||
|
isAccessibilityElement = true
|
||||||
|
accessibilityHint = checkbox.accessibilityHint
|
||||||
|
accessibilityTraits = checkbox.accessibilityTraits
|
||||||
|
observation = observe(\.checkbox.isSelected, options: [.new]) { [weak self] _, _ in
|
||||||
|
self?.updateAccessibilityLabel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc override open func updateView(_ size: CGFloat) {
|
@objc override open func updateView(_ size: CGFloat) {
|
||||||
@ -111,6 +118,7 @@
|
|||||||
|
|
||||||
checkbox.set(with: checkBoxWithLabelModel.checkbox, delegateObject, additionalData)
|
checkbox.set(with: checkBoxWithLabelModel.checkbox, delegateObject, additionalData)
|
||||||
label.set(with: checkBoxWithLabelModel.label, delegateObject, additionalData)
|
label.set(with: checkBoxWithLabelModel.label, delegateObject, additionalData)
|
||||||
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||||
@ -124,4 +132,13 @@
|
|||||||
checkbox.reset()
|
checkbox.reset()
|
||||||
alignCheckbox(.center)
|
alignCheckbox(.center)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override open func accessibilityActivate() -> Bool {
|
||||||
|
checkbox.accessibilityActivate()
|
||||||
|
}
|
||||||
|
|
||||||
|
open func updateAccessibilityLabel() {
|
||||||
|
checkbox.updateAccessibilityLabel()
|
||||||
|
accessibilityLabel = [checkbox.accessibilityLabel, label.text].compactMap { $0 }.joined(separator: ",")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ public enum CheckboxPosition: String, Codable {
|
|||||||
case bottom
|
case bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
@objcMembers open class CheckboxLabelModel: MoleculeModelProtocol {
|
@objcMembers open class CheckboxLabelModel: MoleculeModelProtocol, ParentMoleculeModelProtocol {
|
||||||
open class var identifier: String { "checkboxLabel" }
|
open class var identifier: String { "checkboxLabel" }
|
||||||
public var moleculeName: String = CheckboxLabelModel.identifier
|
public var moleculeName: String = CheckboxLabelModel.identifier
|
||||||
@DecodableDefault.UUIDString public var id: String
|
@DecodableDefault.UUIDString public var id: String
|
||||||
@ -25,6 +25,7 @@ public enum CheckboxPosition: String, Codable {
|
|||||||
public var checkbox: CheckboxModel
|
public var checkbox: CheckboxModel
|
||||||
public var label: LabelModel
|
public var label: LabelModel
|
||||||
|
|
||||||
|
public var children: [MoleculeModelProtocol] { [checkbox, label] }
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -64,7 +64,7 @@ public class FormLabelModel: EnableableModelProtocol {
|
|||||||
if enabled {
|
if enabled {
|
||||||
required.attributes = [LabelAttributeColorModel(FormLabelModel.defaultRequiredTextColor, model.text.count + 1, 8)]
|
required.attributes = [LabelAttributeColorModel(FormLabelModel.defaultRequiredTextColor, model.text.count + 1, 8)]
|
||||||
}
|
}
|
||||||
required.text = "\(model.text) Optional"
|
required.text = "\(model.text) \(MVMCoreUIUtility.hardcodedString(withKey: "textfield_optional") ?? "")"
|
||||||
return required
|
return required
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,7 @@ public typealias ActionBlock = () -> ()
|
|||||||
|
|
||||||
public var shouldMaskWhileRecording: Bool = false
|
public var shouldMaskWhileRecording: Bool = false
|
||||||
|
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
// MARK: - Multi-Action Text
|
// MARK: - Multi-Action Text
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
@ -230,7 +231,7 @@ public typealias ActionBlock = () -> ()
|
|||||||
documentAttributes: nil)
|
documentAttributes: nil)
|
||||||
} catch {
|
} catch {
|
||||||
if let coreErrorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "LabelHTMLParse") {
|
if let coreErrorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "LabelHTMLParse") {
|
||||||
MVMCoreUILoggingHandler.addError(toLog: coreErrorObject)
|
MVMCoreUILoggingHandler.shared()?.addError(toLog: coreErrorObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,6 +409,7 @@ public typealias ActionBlock = () -> ()
|
|||||||
attributedText = attributedString
|
attributedText = attributedString
|
||||||
originalAttributedString = attributedText
|
originalAttributedString = attributedText
|
||||||
}
|
}
|
||||||
|
self.model = labelModel
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||||
@ -1011,17 +1013,13 @@ extension Label {
|
|||||||
|
|
||||||
func validateAttribute(range: NSRange, in string: NSAttributedString, type: String = "") -> NSRange? {
|
func validateAttribute(range: NSRange, in string: NSAttributedString, type: String = "") -> NSRange? {
|
||||||
guard range.location >= 0 && range.location <= string.length else {
|
guard range.location >= 0 && range.location <= string.length else {
|
||||||
if let loggingHandler = MVMCoreLoggingHandler.shared(), loggingHandler.responds(to: #selector(MVMCoreLoggingHandler.addError(toLog:))) {
|
MVMCoreLoggingHandler.shared()?.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Attribute starting location \(range.lowerBound) is out of bounds for '\(string.string)'. Attribute is discarded.", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: "\(#file): \(#function)")!)
|
||||||
loggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Attribute starting location \(range.lowerBound) is out of bounds for '\(string.string)'. Attribute is discarded.", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: "\(#file): \(#function)")!)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if type != "image" && range.upperBound > string.length {
|
if type != "image" && range.upperBound > string.length {
|
||||||
let newRange = NSRange(location: range.location, length: string.length - range.location)
|
let newRange = NSRange(location: range.location, length: string.length - range.location)
|
||||||
if let loggingHandler = MVMCoreLoggingHandler.shared(), loggingHandler.responds(to: #selector(MVMCoreLoggingHandler.addError(toLog:))) {
|
MVMCoreLoggingHandler.shared()?.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Attribute ending location \(range.upperBound) is out of bounds for '\(string)'. Adjusting to \(newRange.upperBound).", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: "\(#file): \(#function)")!)
|
||||||
loggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Attribute ending location \(range.upperBound) is out of bounds for '\(string)'. Adjusting to \(newRange.upperBound).", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: "\(#file): \(#function)")!)
|
|
||||||
}
|
|
||||||
return newRange
|
return newRange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,116 +7,112 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import VDS
|
||||||
|
|
||||||
|
@objcMembers open class Line: VDS.Line, VDSMoleculeViewProtocol {
|
||||||
@objcMembers open class Line: View {
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
open var viewModel: LineModel!
|
||||||
|
open var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
open var additionalData: [AnyHashable : Any]?
|
||||||
|
|
||||||
var lineModel: LineModel? {
|
open override var orientation: Line.Orientation {
|
||||||
get { return model as? LineModel }
|
didSet {
|
||||||
}
|
if orientation == .horizontal {
|
||||||
|
setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
//--------------------------------------------------
|
setContentHuggingPriority(.required, for: .vertical)
|
||||||
// MARK: - Constraints
|
setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||||
//--------------------------------------------------
|
setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
} else {
|
||||||
public var heightConstraint: NSLayoutConstraint?
|
setContentHuggingPriority(.required, for: .horizontal)
|
||||||
public var widthConstraint: NSLayoutConstraint?
|
setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||||
|
setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
open func updateLineConstraints(constant: CGFloat) {
|
setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
||||||
if let useVerticalLine = lineModel?.useVerticalLine, useVerticalLine {
|
}
|
||||||
heightConstraint?.isActive = false
|
|
||||||
widthConstraint?.isActive = true
|
|
||||||
widthConstraint?.constant = constant
|
|
||||||
} else {
|
|
||||||
widthConstraint?.isActive = false
|
|
||||||
heightConstraint?.isActive = true
|
|
||||||
heightConstraint?.constant = constant
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) {
|
public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) {
|
||||||
self.init(frame: .zero)
|
self.init(frame: .zero)
|
||||||
addLine(to: view, edge: edge, useMargin: useMargin)
|
addLine(to: view, edge: edge, useMargin: useMargin)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init() {
|
public required init() {
|
||||||
super.init(frame: .zero)
|
super.init()
|
||||||
model = LineModel(type: .secondary)
|
viewModel = LineModel(type: .secondary)
|
||||||
setStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
model = LineModel(type: .secondary)
|
viewModel = LineModel(type: .secondary)
|
||||||
setStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init?(coder: NSCoder) {
|
public required init?(coder: NSCoder) {
|
||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
model = LineModel(type: .secondary)
|
viewModel = LineModel(type: .secondary)
|
||||||
setStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
|
|
||||||
super.init(model: model, delegateObject, additionalData)
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open func setStyle(_ style: LineModel.Style) {
|
|
||||||
lineModel?.type = style
|
|
||||||
backgroundColor = lineModel?.backgroundColor?.uiColor
|
|
||||||
updateLineConstraints(constant: lineModel?.thickness ?? 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func shouldBeVisible() -> Bool {
|
open func shouldBeVisible() -> Bool {
|
||||||
guard let type = lineModel?.type else { return false }
|
guard let type = viewModel?.type else { return false }
|
||||||
return type != .none
|
return type != .none
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
open func setStyle(_ style: LineModel.Style) {
|
||||||
// MARK: - MoleculeViewProtocol
|
viewModel.type = style
|
||||||
//--------------------------------------------------
|
update(viewModel: viewModel)
|
||||||
|
}
|
||||||
|
|
||||||
open func addLine(to view: UIView, edge: UIRectEdge, useMargin: Bool) {
|
open func addLine(to view: UIView, edge: UIRectEdge, useMargin: Bool) {
|
||||||
view.addSubview(self)
|
view.addSubview(self)
|
||||||
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: self, useMargins: useMargin, pinTop: edge != .bottom, pinBottom: edge != .top, pinLeft: edge != .right, pinRight: edge != .left).values))
|
NSLayoutConstraint.activate(
|
||||||
|
Array(
|
||||||
|
NSLayoutConstraint.pinView(toSuperview: self,
|
||||||
|
useMargins: useMargin,
|
||||||
|
pinTop: edge != .bottom,
|
||||||
|
pinBottom: edge != .top,
|
||||||
|
pinLeft: edge != .right,
|
||||||
|
pinRight: edge != .left).values
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func setupView() {
|
open override func draw(_ rect: CGRect) {
|
||||||
super.setupView()
|
guard viewModel.type != .none else { return }
|
||||||
heightConstraint = heightAnchor.constraint(equalToConstant: 1)
|
super.draw(rect)
|
||||||
heightConstraint?.isActive = true
|
|
||||||
widthConstraint = widthAnchor.constraint(equalToConstant: 1)
|
|
||||||
widthConstraint?.isActive = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
//--------------------------------------------------
|
||||||
super.set(with: model, delegateObject, additionalData)
|
// MARK: - VDSMoleculeViewProtocol
|
||||||
|
//--------------------------------------------------
|
||||||
|
open func viewModelDidUpdate() {
|
||||||
|
surface = viewModel.surface
|
||||||
|
style = VDS.Line.Style(rawValue: viewModel.type.rawValue) ?? .primary
|
||||||
|
orientation = viewModel.orientation
|
||||||
|
}
|
||||||
|
|
||||||
if let lineModel = model as? LineModel {
|
//--------------------------------------------------
|
||||||
setStyle(lineModel.type)
|
// MARK: - MoleculeViewProtocol
|
||||||
}
|
//--------------------------------------------------
|
||||||
|
public func updateView(_ size: CGFloat) {
|
||||||
|
setNeedsDisplay()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func reset() {
|
public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||||
setStyle(.secondary)
|
return 1
|
||||||
}
|
|
||||||
|
|
||||||
public override static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
|
||||||
return (model as? LineModel)?.thickness ?? 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||||
|
//--------------------------------------------------
|
||||||
extension Line: MVMCoreUIViewConstrainingProtocol {
|
extension Line: MVMCoreUIViewConstrainingProtocol {
|
||||||
|
|
||||||
open func needsToBeConstrained() -> Bool {
|
open func needsToBeConstrained() -> Bool {
|
||||||
|
|||||||
@ -8,8 +8,9 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
import VDS
|
||||||
|
|
||||||
@objcMembers public class LineModel: MoleculeModelProtocol {
|
public class LineModel: MoleculeModelProtocol, Invertable {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Enums
|
// MARK: - Enums
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -30,12 +31,12 @@ import VDSColorTokens
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
The style of the line:
|
The style of the line:
|
||||||
- secondary (1 height, silver)
|
- secondary (VDS Secondary)
|
||||||
- primary (1 height, black)
|
- primary (VDS Primary)
|
||||||
- standard (1 height, silver) - deprecated
|
- standard (VDS Secondary) - deprecated
|
||||||
- thin (1 height, black) - deprecated
|
- thin (VDS Primar) - deprecated
|
||||||
- medium (2 height, black)
|
- medium (VDS Primar)
|
||||||
- heavy (4 height, black)
|
- heavy (VDS Primar)
|
||||||
- none (hidden)
|
- none (hidden)
|
||||||
*/
|
*/
|
||||||
public enum Style: String, Codable {
|
public enum Style: String, Codable {
|
||||||
@ -54,53 +55,14 @@ import VDSColorTokens
|
|||||||
|
|
||||||
public static var identifier: String = "line"
|
public static var identifier: String = "line"
|
||||||
public var id: String = UUID().uuidString
|
public var id: String = UUID().uuidString
|
||||||
|
public var backgroundColor: Color?
|
||||||
|
|
||||||
public var type: Style = .secondary
|
public var type: Style = .secondary
|
||||||
public var frequency: Frequency? = .allExceptTop
|
public var frequency: Frequency? = .allExceptTop
|
||||||
|
|
||||||
//TODO: use color insted of backgroundColor. Needs server changes
|
|
||||||
// public var color: Color?
|
|
||||||
private var _backgroundColor: Color?
|
|
||||||
public var backgroundColor: Color? {
|
|
||||||
get {
|
|
||||||
if let backgroundColor = _backgroundColor { return backgroundColor }
|
|
||||||
if inverted {
|
|
||||||
if type == .secondary || type == .standard { return Color(uiColor: VDSColor.paletteGray20) }
|
|
||||||
return Color(uiColor: VDSColor.elementsPrimaryOndark)
|
|
||||||
}
|
|
||||||
if type == .secondary || type == .standard { return Color(uiColor: VDSColor.paletteGray85) }
|
|
||||||
return Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_backgroundColor = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var _thickness: CGFloat?
|
|
||||||
public var thickness: CGFloat {
|
|
||||||
get {
|
|
||||||
if let thickness = _thickness { return thickness }
|
|
||||||
switch type {
|
|
||||||
case .heavy:
|
|
||||||
return 4
|
|
||||||
case .medium:
|
|
||||||
return 2
|
|
||||||
case .none:
|
|
||||||
return 0
|
|
||||||
default:
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_thickness = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var inverted: Bool = false
|
public var inverted: Bool = false
|
||||||
|
|
||||||
// Use this to show vertical line
|
public var orientation: VDS.Line.Orientation = .horizontal
|
||||||
// Default is false
|
|
||||||
public var useVerticalLine: Bool?
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
@ -108,13 +70,11 @@ import VDSColorTokens
|
|||||||
|
|
||||||
public init(type: Style) {
|
public init(type: Style) {
|
||||||
self.type = type
|
self.type = type
|
||||||
self.useVerticalLine = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(verticalLineOf type: Style, backgroundColor: Color? = nil) {
|
public init(verticalLineOf type: Style) {
|
||||||
self.type = type
|
self.type = type
|
||||||
self.backgroundColor = backgroundColor
|
orientation = .vertical
|
||||||
self.useVerticalLine = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -125,13 +85,10 @@ import VDSColorTokens
|
|||||||
case id
|
case id
|
||||||
case moleculeName
|
case moleculeName
|
||||||
case type
|
case type
|
||||||
case backgroundColor
|
|
||||||
case backgroundColor_inverted
|
|
||||||
case color
|
|
||||||
case frequency
|
case frequency
|
||||||
case inverted
|
case inverted
|
||||||
case useVerticalLine
|
case useVerticalLine
|
||||||
case thickness
|
case orientation
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -155,9 +112,12 @@ import VDSColorTokens
|
|||||||
self.inverted = inverted
|
self.inverted = inverted
|
||||||
}
|
}
|
||||||
|
|
||||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
/// adding code to look for the old useVerticalLine or the new orientation
|
||||||
useVerticalLine = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalLine)
|
if let useVerticalLine = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalLine) {
|
||||||
_thickness = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .thickness)
|
orientation = useVerticalLine ? .vertical : .horizontal
|
||||||
|
} else if let orientation = try typeContainer.decodeIfPresent(VDS.Line.Orientation.self, forKey: .orientation) {
|
||||||
|
self.orientation = orientation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -167,8 +127,6 @@ import VDSColorTokens
|
|||||||
try container.encode(type, forKey: .type)
|
try container.encode(type, forKey: .type)
|
||||||
try container.encode(inverted, forKey: .inverted)
|
try container.encode(inverted, forKey: .inverted)
|
||||||
try container.encodeIfPresent(frequency, forKey: .frequency)
|
try container.encodeIfPresent(frequency, forKey: .frequency)
|
||||||
try container.encodeIfPresent(_backgroundColor, forKey: .backgroundColor)
|
try container.encode(orientation == .vertical, forKey: .useVerticalLine)
|
||||||
try container.encodeIfPresent(useVerticalLine, forKey: .useVerticalLine)
|
|
||||||
try container.encodeIfPresent(_thickness, forKey: .thickness)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,8 +13,11 @@ import Foundation
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
var progressBarModel: ProgressBarModel?
|
public var progressBarModel: ProgressBarModel? {
|
||||||
|
get { model as? ProgressBarModel }
|
||||||
|
set { model = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
var thickness: CGFloat = 8.0 {
|
var thickness: CGFloat = 8.0 {
|
||||||
willSet(newValue) {
|
willSet(newValue) {
|
||||||
|
|||||||
@ -18,7 +18,13 @@ open class Tilelet: VDS.Tilelet, VDSMoleculeViewProtocol{
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
public var viewModel: TileletModel!
|
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
|
public var viewModel: TileletModel! {
|
||||||
|
get { model as? TileletModel }
|
||||||
|
set { model = newValue }
|
||||||
|
}
|
||||||
public var delegateObject: MVMCoreUIDelegateObject?
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
public var additionalData: [AnyHashable: Any]?
|
public var additionalData: [AnyHashable: Any]?
|
||||||
|
|
||||||
@ -50,14 +56,13 @@ open class Tilelet: VDS.Tilelet, VDSMoleculeViewProtocol{
|
|||||||
//setup action
|
//setup action
|
||||||
if let action = viewModel.action {
|
if let action = viewModel.action {
|
||||||
//add the subscriber
|
//add the subscriber
|
||||||
onClickSubscriber = publisher(for: .touchUpInside)
|
onClick = { [weak self] control in
|
||||||
.sink {[weak self] control in
|
guard let self, let viewModel = self.viewModel else { return }
|
||||||
guard let self else { return }
|
MVMCoreUIActionHandler.performActionUnstructured(with: action,
|
||||||
MVMCoreUIActionHandler.performActionUnstructured(with: action,
|
sourceModel: viewModel,
|
||||||
sourceModel: self.viewModel,
|
additionalData: self.additionalData,
|
||||||
additionalData: self.additionalData,
|
delegateObject: self.delegateObject)
|
||||||
delegateObject: self.delegateObject)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@ open class Video: View {
|
|||||||
})
|
})
|
||||||
case .failed:
|
case .failed:
|
||||||
if let errorObject = item.loadFailedError {
|
if let errorObject = item.loadFailedError {
|
||||||
MVMCoreLoggingHandler.addError(toLog: errorObject)
|
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -16,3 +16,11 @@ extension Icon.Size: Codable {}
|
|||||||
extension TileContainer.BackgroundColor: Codable {}
|
extension TileContainer.BackgroundColor: Codable {}
|
||||||
extension TileContainer.Padding: Codable {}
|
extension TileContainer.Padding: Codable {}
|
||||||
extension TileContainer.AspectRatio: Codable {}
|
extension TileContainer.AspectRatio: Codable {}
|
||||||
|
extension TextLink.Size: Codable {}
|
||||||
|
extension VDS.Line.Style: Codable {}
|
||||||
|
extension VDS.Line.Orientation: Codable {}
|
||||||
|
extension Use: Codable {}
|
||||||
|
extension VDS.Button.Size: RawRepresentableCodable {
|
||||||
|
public static var mapping: [String : VDS.Button.Size] { ["standard": .large, "tiny": .small] }
|
||||||
|
public static var defaultValue: VDS.Button.Size? { nil }
|
||||||
|
}
|
||||||
@ -42,6 +42,13 @@ import Foundation
|
|||||||
return Stack<StackModel>.createStack(with: [headline, horizontalStack], spacing: 8)
|
return Stack<StackModel>.createStack(with: [headline, horizontalStack], spacing: 8)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------------
|
||||||
|
// MARK: - Constraints
|
||||||
|
//-------------------------------------------------------
|
||||||
|
public var verticalLine1HeightConstraint: NSLayoutConstraint?
|
||||||
|
public var verticalLine2HeightConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
//-------------------------------------------------------
|
//-------------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//-------------------------------------------------------
|
//-------------------------------------------------------
|
||||||
@ -51,11 +58,10 @@ import Foundation
|
|||||||
body.lineBreakMode = .byTruncatingTail
|
body.lineBreakMode = .byTruncatingTail
|
||||||
body2.lineBreakMode = .byTruncatingTail
|
body2.lineBreakMode = .byTruncatingTail
|
||||||
body3.lineBreakMode = .byTruncatingTail
|
body3.lineBreakMode = .byTruncatingTail
|
||||||
|
|
||||||
verticalLine1.widthConstraint?.isActive = true
|
// setup lines
|
||||||
verticalLine1.backgroundColor = .mvmBlack
|
verticalLine1.orientation = .vertical
|
||||||
verticalLine2.widthConstraint?.isActive = true
|
verticalLine2.orientation = .vertical
|
||||||
verticalLine2.backgroundColor = .mvmBlack
|
|
||||||
|
|
||||||
addMolecule(stack)
|
addMolecule(stack)
|
||||||
stack.restack()
|
stack.restack()
|
||||||
@ -80,13 +86,13 @@ import Foundation
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func setLineHeight() {
|
open func setLineHeight() {
|
||||||
verticalLine1.heightConstraint?.isActive = false
|
verticalLine1HeightConstraint?.isActive = false
|
||||||
verticalLine1.heightConstraint = verticalLine1.heightAnchor.constraint(equalTo: body2.heightAnchor, multiplier: 1)
|
verticalLine1HeightConstraint = verticalLine1.heightAnchor.constraint(equalTo: body2.heightAnchor, multiplier: 1)
|
||||||
verticalLine1.heightConstraint?.isActive = true
|
verticalLine1HeightConstraint?.isActive = true
|
||||||
|
|
||||||
verticalLine2.heightConstraint?.isActive = false
|
verticalLine2HeightConstraint?.isActive = false
|
||||||
verticalLine2.heightConstraint = verticalLine2.heightAnchor.constraint(equalTo: body3.heightAnchor, multiplier: 1)
|
verticalLine2HeightConstraint = verticalLine2.heightAnchor.constraint(equalTo: body3.heightAnchor, multiplier: 1)
|
||||||
verticalLine2.heightConstraint?.isActive = true
|
verticalLine2HeightConstraint?.isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class ListDeviceComplexButtonMediumModel: ListItemModel, MoleculeModelPro
|
|||||||
|
|
||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
button.size = .tiny
|
button.size = .small
|
||||||
button.style = .secondary
|
button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class ListDeviceComplexButtonSmallModel: ListItemModel, MoleculeModelProt
|
|||||||
|
|
||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
button.size = .tiny
|
button.size = .small
|
||||||
button.style = .secondary
|
button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var stack: Stack<StackModel>
|
public var stack: Stack<StackModel>
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
private var observation: NSKeyValueObservation? = nil
|
private var observation: NSKeyValueObservation? = nil
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -68,6 +68,7 @@
|
|||||||
|
|
||||||
checkbox.set(with: model.checkbox, delegateObject, additionalData)
|
checkbox.set(with: model.checkbox, delegateObject, additionalData)
|
||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +94,7 @@
|
|||||||
message += label
|
message += label
|
||||||
}
|
}
|
||||||
|
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListLeftVariableCheckboxAllTextAndLinksModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
if !linkShowing {
|
if !linkShowing {
|
||||||
// Make whole cell focusable if no link.
|
// Make whole cell focusable if no link.
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
public let leftImage = LoadImageView(pinnedEdges: .all)
|
public let leftImage = LoadImageView(pinnedEdges: .all)
|
||||||
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
||||||
public var stack: Stack<StackModel>
|
public var stack: Stack<StackModel>
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -53,6 +54,7 @@
|
|||||||
|
|
||||||
leftImage.set(with: model.image, delegateObject, additionalData)
|
leftImage.set(with: model.image, delegateObject, additionalData)
|
||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +77,7 @@
|
|||||||
|
|
||||||
func updateAccessibilityLabel() {
|
func updateAccessibilityLabel() {
|
||||||
|
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListLeftVariableIconAllTextLinksModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
public let leftImage = LoadImageView(pinnedEdges: .all)
|
public let leftImage = LoadImageView(pinnedEdges: .all)
|
||||||
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink(spacing: 2.0)
|
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink(spacing: 2.0)
|
||||||
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
public lazy var rightLabelStackItem: StackItem = {
|
public lazy var rightLabelStackItem: StackItem = {
|
||||||
return StackItem(andContain: rightLabel)
|
return StackItem(andContain: rightLabel)
|
||||||
}()
|
}()
|
||||||
@ -66,6 +66,7 @@
|
|||||||
leftImage.set(with: model.image, delegateObject, additionalData)
|
leftImage.set(with: model.image, delegateObject, additionalData)
|
||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
|
rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +98,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateAccessibilityLabel() {
|
func updateAccessibilityLabel() {
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListLeftVariableIconWithRightCaretAllTextLinksModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
public let leftLabel = Label(fontStyle: .Title2XLarge)
|
public let leftLabel = Label(fontStyle: .Title2XLarge)
|
||||||
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
||||||
public var stack: Stack<StackModel>
|
public var stack: Stack<StackModel>
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -54,6 +55,7 @@
|
|||||||
|
|
||||||
leftLabel.text = String(model.number)
|
leftLabel.text = String(model.number)
|
||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +87,7 @@
|
|||||||
|
|
||||||
func updateAccessibilityLabel() {
|
func updateAccessibilityLabel() {
|
||||||
|
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListLeftVariableNumberedListAllTextAndLinksModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
let radioButton = RadioButton()
|
let radioButton = RadioButton()
|
||||||
let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
||||||
var stack: Stack<StackModel>
|
var stack: Stack<StackModel>
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
private var observation: NSKeyValueObservation? = nil
|
private var observation: NSKeyValueObservation? = nil
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
@ -62,6 +62,7 @@
|
|||||||
|
|
||||||
radioButton.set(with: model.radioButton, delegateObject, additionalData)
|
radioButton.set(with: model.radioButton, delegateObject, additionalData)
|
||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +92,7 @@
|
|||||||
message += label
|
message += label
|
||||||
}
|
}
|
||||||
|
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListLeftVariableRadioButtonAllTextAndLinksModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
if !linkShowing {
|
if !linkShowing {
|
||||||
// Make whole cell focusable if no link.
|
// Make whole cell focusable if no link.
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import UIKit
|
|||||||
let leftImage = LoadImageView(pinnedEdges: .all)
|
let leftImage = LoadImageView(pinnedEdges: .all)
|
||||||
let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
||||||
var stack: Stack<StackModel>
|
var stack: Stack<StackModel>
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
private var observation: NSKeyValueObservation? = nil
|
private var observation: NSKeyValueObservation? = nil
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
@ -79,6 +79,7 @@ import UIKit
|
|||||||
radioButton.set(with: model.radioButton, delegateObject, additionalData)
|
radioButton.set(with: model.radioButton, delegateObject, additionalData)
|
||||||
leftImage.set(with: model.image, delegateObject, additionalData)
|
leftImage.set(with: model.image, delegateObject, additionalData)
|
||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +113,7 @@ import UIKit
|
|||||||
message += label
|
message += label
|
||||||
}
|
}
|
||||||
|
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListLeftVariableRadioButtonAndPaymentMethodModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
if !linkShowing {
|
if !linkShowing {
|
||||||
// Make whole cell focusable if no link.
|
// Make whole cell focusable if no link.
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
@objcMembers open class ListProgressBarThin: TableViewCell {
|
open class ListProgressBarThin: TableViewCell {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Outlets
|
// MARK: - Outlets
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -15,7 +15,11 @@
|
|||||||
public let progressBar = ProgressBar()
|
public let progressBar = ProgressBar()
|
||||||
public let leftHeadline = Label(fontStyle: .BoldBodySmall)
|
public let leftHeadline = Label(fontStyle: .BoldBodySmall)
|
||||||
public let leftBody = Label(fontStyle: .BoldBodySmall)
|
public let leftBody = Label(fontStyle: .BoldBodySmall)
|
||||||
public let rightBar = Line()
|
public let rightBar: DataLine = {
|
||||||
|
var line = DataLine()
|
||||||
|
line.heightConstraint.constant = 2
|
||||||
|
return line
|
||||||
|
}()
|
||||||
public let rightLabel = Label(fontStyle: .BoldBodySmall)
|
public let rightLabel = Label(fontStyle: .BoldBodySmall)
|
||||||
private let barStackItem: StackItem
|
private let barStackItem: StackItem
|
||||||
private let rightLabelStackItem: StackItem
|
private let rightLabelStackItem: StackItem
|
||||||
@ -102,7 +106,6 @@
|
|||||||
leftHeadline.styleB1(true)
|
leftHeadline.styleB1(true)
|
||||||
leftBody.styleB2(true)
|
leftBody.styleB2(true)
|
||||||
rightLabel.styleB2(true)
|
rightLabel.styleB2(true)
|
||||||
rightBar.setStyle(.medium)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
|
|||||||
@ -16,14 +16,14 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
public var progressBar: ProgressBarModel
|
public var progressBar: ProgressBarModel
|
||||||
public var leftHeadline: LabelModel
|
public var leftHeadline: LabelModel
|
||||||
public var leftBody: LabelModel?
|
public var leftBody: LabelModel?
|
||||||
public var rightBar: LineModel
|
public var rightBar: DataLineModel
|
||||||
public var rightLabel: LabelModel
|
public var rightLabel: LabelModel
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public init(progressBar: ProgressBarModel, leftHeadline: LabelModel, leftBody: LabelModel? = nil, rightBar: LineModel, rightLabel: LabelModel) {
|
public init(progressBar: ProgressBarModel, leftHeadline: LabelModel, leftBody: LabelModel? = nil, rightBar: DataLineModel, rightLabel: LabelModel) {
|
||||||
self.progressBar = progressBar
|
self.progressBar = progressBar
|
||||||
self.leftHeadline = leftHeadline
|
self.leftHeadline = leftHeadline
|
||||||
self.leftBody = leftBody
|
self.leftBody = leftBody
|
||||||
@ -38,9 +38,7 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
|
|
||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
|
|
||||||
rightBar.type = .medium
|
|
||||||
|
|
||||||
if rightBar.backgroundColor == nil {
|
if rightBar.backgroundColor == nil {
|
||||||
rightBar.backgroundColor = Color(uiColor: .gray)
|
rightBar.backgroundColor = Color(uiColor: .gray)
|
||||||
}
|
}
|
||||||
@ -74,7 +72,7 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
progressBar = try typeContainer.decode(ProgressBarModel.self, forKey:.progressBar)
|
progressBar = try typeContainer.decode(ProgressBarModel.self, forKey:.progressBar)
|
||||||
leftHeadline = try typeContainer.decode(LabelModel.self, forKey: .leftHeadline)
|
leftHeadline = try typeContainer.decode(LabelModel.self, forKey: .leftHeadline)
|
||||||
leftBody = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .leftBody)
|
leftBody = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .leftBody)
|
||||||
rightBar = try typeContainer.decode(LineModel.self, forKey: .rightBar)
|
rightBar = try typeContainer.decode(DataLineModel.self, forKey: .rightBar)
|
||||||
rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel)
|
rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel)
|
||||||
try super.init(from: decoder)
|
try super.init(from: decoder)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,143 +11,73 @@
|
|||||||
// MARK: - Outlets
|
// MARK: - Outlets
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public let heart = Heart()
|
public let badge = Badge()
|
||||||
public let leftHeadline = Label(fontStyle: .BoldBodySmall)
|
public let leftHeadline = Label(fontStyle: .BoldTitleSmall)
|
||||||
public let leftBody = Label(fontStyle: .RegularBodySmall)
|
public let leftBody = Label(fontStyle: .RegularMicro)
|
||||||
public let leftSubBody = Label(fontStyle: .RegularBodySmall)
|
public let leftSubBody = Label(fontStyle: .RegularBodySmall)
|
||||||
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
||||||
private lazy var rightLabelStackItem: StackItem = {
|
public var model: ListStoreLocatorModel?
|
||||||
StackItem(andContain: rightLabel)
|
|
||||||
}()
|
|
||||||
|
|
||||||
public lazy var horizontalStack: Stack<StackModel> = {
|
public lazy var horizontalStack: Stack<StackModel> = {
|
||||||
return Stack<StackModel>(with: StackModel(molecules: [StackItemModel(horizontalAlignment: .fill),
|
return Stack<StackModel>(with: StackModel(molecules: [StackItemModel(horizontalAlignment: .fill),
|
||||||
StackItemModel(horizontalAlignment: .fill),
|
StackItemModel(horizontalAlignment: .trailing, verticalAlignment: .center)],
|
||||||
StackItemModel(horizontalAlignment: .trailing)],
|
axis: .horizontal, spacing: Padding.Two), stackItems: [StackItem(andContain: stack), StackItem(andContain: rightLabel)])
|
||||||
axis: .horizontal, spacing: Padding.Two), stackItems: [StackItem(andContain: leftHeadline), StackItem(andContain: heart), rightLabelStackItem])
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
public lazy var stack: Stack<StackModel> = {
|
public lazy var stack: Stack<StackModel> = {
|
||||||
return Stack<StackModel>.createStack(with: [horizontalStack, leftBody, leftSubBody], axis: .vertical, spacing: 0)
|
return Stack<StackModel>(with: .init(molecules: [StackItemModel(horizontalAlignment: .leading),
|
||||||
}()
|
StackItemModel(horizontalAlignment: .fill), StackItemModel(horizontalAlignment: .fill), StackItemModel(horizontalAlignment: .fill)],
|
||||||
public var sizeObject: MFSizeObject? = MFSizeObject(standardSize: 12, standardiPadPortraitSize: 18)
|
axis: .vertical, spacing: Padding.One), stackItems: [StackItem(andContain: badge), StackItem(andContain: leftHeadline), StackItem(andContain: leftBody), StackItem(andContain: leftSubBody)])
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
//-------------------------------------------------------
|
//-------------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//-------------------------------------------------------
|
//-------------------------------------------------------
|
||||||
open override func setupView() {
|
open override func setupView() {
|
||||||
super.setupView()
|
super.setupView()
|
||||||
|
|
||||||
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
|
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
|
||||||
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
|
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
|
||||||
addMolecule(stack)
|
addMolecule(horizontalStack)
|
||||||
stack.restack()
|
stack.restack()
|
||||||
horizontalStack.restack()
|
horizontalStack.restack()
|
||||||
|
leftSubBody.textColor = UIColor.mfTextLightGray()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func updateView(_ size: CGFloat) {
|
|
||||||
super.updateView(size)
|
|
||||||
if let dimension = sizeObject?.getValueBased(onSize: size) {
|
|
||||||
heart.widthConstraint?.constant = dimension
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
// MARK: - Molecule
|
// MARK: - Molecule
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
super.set(with: model, delegateObject, additionalData)
|
super.set(with: model, delegateObject, additionalData)
|
||||||
|
|
||||||
guard let model = model as? ListStoreLocatorModel else { return }
|
guard let model = model as? ListStoreLocatorModel else { return }
|
||||||
horizontalStack.updateContainedMolecules(with: [model.leftHeadline, model.heart, model.rightLabel], delegateObject, additionalData)
|
stack.updateContainedMolecules(with: [model.badge, model.leftHeadline, model.leftBody, model.leftSubBody], delegateObject, additionalData)
|
||||||
leftBody.set(with: model.leftBody, delegateObject, additionalData)
|
rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
|
||||||
leftSubBody.set(with: model.leftSubBody, delegateObject, additionalData)
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func alignAccessoryToHero() -> CGPoint? {
|
|
||||||
let heroCenter = super.alignAccessoryToHero()
|
|
||||||
|
|
||||||
if let heroCenter = heroCenter {
|
|
||||||
let convertedPoint = horizontalStack.convert(heroCenter, from: self)
|
|
||||||
rightLabelStackItem.containerHelper.alignCenterVerticalConstraint?.constant = convertedPoint.y - horizontalStack.bounds.midY
|
|
||||||
}
|
|
||||||
return heroCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
|
||||||
if listItemModel?.action != nil {
|
|
||||||
super.didSelectCell(at: index, delegateObject: delegateObject, additionalData: additionalData)
|
|
||||||
} else {
|
|
||||||
heart.tapAction()
|
|
||||||
updateAccessibilityLabel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 120 }
|
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 120 }
|
||||||
|
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
leftHeadline.setFontStyle(.BoldBodySmall)
|
leftHeadline.setFontStyle(.BoldTitleSmall)
|
||||||
leftBody.setFontStyle(.RegularBodySmall)
|
leftBody.setFontStyle(.RegularMicro)
|
||||||
leftSubBody.setFontStyle(.RegularBodySmall)
|
leftSubBody.setFontStyle(.RegularBodySmall)
|
||||||
rightLabel.setFontStyle(.RegularBodySmall)
|
rightLabel.setFontStyle(.RegularBodySmall)
|
||||||
|
leftSubBody.textColor = UIColor.mfTextLightGray()
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Accessibility
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
func getAccessibilityMessage() -> String? {
|
|
||||||
var message = ""
|
|
||||||
heart.updateAccessibilityLabel()
|
|
||||||
|
|
||||||
if let leftHeadlineText = leftHeadline.text, !leftHeadlineText.isEmpty {
|
|
||||||
message += leftHeadlineText + ", "
|
|
||||||
}
|
|
||||||
|
|
||||||
if let leftBodyText = leftBody.text, !leftBodyText.isEmpty {
|
|
||||||
message += leftBodyText + ", "
|
|
||||||
}
|
|
||||||
|
|
||||||
if let leftSubBodyText = leftSubBody.text, !leftSubBodyText.isEmpty {
|
|
||||||
message += leftSubBodyText + ", "
|
|
||||||
}
|
|
||||||
|
|
||||||
if let rightLabelText = rightLabel.text, !rightLabelText.isEmpty {
|
|
||||||
message += rightLabelText
|
|
||||||
}
|
|
||||||
return message.count > 0 ? message : nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateAccessibilityLabel() {
|
|
||||||
let hasHeart = !(horizontalStack.stackModel?.molecules[1].gone ?? true)
|
|
||||||
if let accessoryView = accessoryView,
|
|
||||||
hasHeart {
|
|
||||||
// Both accessory and heart actions.
|
|
||||||
isAccessibilityElement = false
|
|
||||||
accessoryView.accessibilityLabel = getAccessibilityMessage()
|
|
||||||
accessibilityElements = [accessoryView, heart]
|
|
||||||
} else {
|
|
||||||
// Make whole cell focusable if no action.
|
|
||||||
isAccessibilityElement = true
|
|
||||||
var message = getAccessibilityMessage()
|
|
||||||
if hasHeart {
|
|
||||||
accessibilityHint = heart.accessibilityHint
|
|
||||||
if let heartLabel = heart.accessibilityLabel {
|
|
||||||
message = (message ?? "") + ", " + heartLabel
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
accessibilityHint = nil
|
|
||||||
}
|
|
||||||
accessibilityLabel = message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensures voice over does not read "selected" after user triggers action on cell.
|
|
||||||
override public var accessibilityTraits: UIAccessibilityTraits {
|
override public var accessibilityTraits: UIAccessibilityTraits {
|
||||||
get {
|
get {
|
||||||
return (listItemModel?.accessibilityTraits) ?? ((accessoryView != nil) ? .button : .none)
|
return (listItemModel?.accessibilityTraits) ?? ((accessoryView != nil) ? .button : .none)
|
||||||
}
|
}
|
||||||
set { }
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateAccessibilityLabel() {
|
||||||
|
isAccessibilityElement = true
|
||||||
|
let message = [model?.badge?.text, model?.leftHeadline.text, model?.leftBody.text, model?.leftSubBody.text, model?.rightLabel.text].compactMap { $0 }.joined(separator: ", ")
|
||||||
|
accessibilityLabel = message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public static var identifier = "listStoreLocator"
|
public static var identifier = "listStoreLocator"
|
||||||
public var heart: HeartModel?
|
public var badge: BadgeModel?
|
||||||
public var leftHeadline: LabelModel
|
public var leftHeadline: LabelModel
|
||||||
public var leftBody: LabelModel
|
public var leftBody: LabelModel
|
||||||
public var leftSubBody: LabelModel
|
public var leftSubBody: LabelModel
|
||||||
@ -22,8 +22,8 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public init(heart: HeartModel?, leftHeadline: LabelModel, leftBody: LabelModel, leftSubBody: LabelModel, rightLabel: LabelModel) {
|
public init(badge: BadgeModel?, leftHeadline: LabelModel, leftBody: LabelModel, leftSubBody: LabelModel, rightLabel: LabelModel) {
|
||||||
self.heart = heart
|
self.badge = badge
|
||||||
self.leftHeadline = leftHeadline
|
self.leftHeadline = leftHeadline
|
||||||
self.leftBody = leftBody
|
self.leftBody = leftBody
|
||||||
self.leftSubBody = leftSubBody
|
self.leftSubBody = leftSubBody
|
||||||
@ -49,7 +49,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case moleculeName
|
case moleculeName
|
||||||
case heart
|
case badge
|
||||||
case leftHeadline
|
case leftHeadline
|
||||||
case leftBody
|
case leftBody
|
||||||
case leftSubBody
|
case leftSubBody
|
||||||
@ -62,7 +62,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
|
|
||||||
public required init(from decoder: Decoder) throws {
|
public required init(from decoder: Decoder) throws {
|
||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
heart = try typeContainer.decodeIfPresent(HeartModel.self, forKey:.heart)
|
badge = try typeContainer.decodeIfPresent(BadgeModel.self, forKey:.badge)
|
||||||
leftHeadline = try typeContainer.decode(LabelModel.self, forKey: .leftHeadline)
|
leftHeadline = try typeContainer.decode(LabelModel.self, forKey: .leftHeadline)
|
||||||
leftBody = try typeContainer.decode(LabelModel.self, forKey: .leftBody)
|
leftBody = try typeContainer.decode(LabelModel.self, forKey: .leftBody)
|
||||||
leftSubBody = try typeContainer.decode(LabelModel.self, forKey: .leftSubBody)
|
leftSubBody = try typeContainer.decode(LabelModel.self, forKey: .leftSubBody)
|
||||||
@ -74,7 +74,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
try super.encode(to: encoder)
|
try super.encode(to: encoder)
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
try container.encode(moleculeName, forKey: .moleculeName)
|
try container.encode(moleculeName, forKey: .moleculeName)
|
||||||
try container.encodeIfPresent(heart, forKey: .heart)
|
try container.encodeIfPresent(badge, forKey: .badge)
|
||||||
try container.encode(leftHeadline, forKey: .leftHeadline)
|
try container.encode(leftHeadline, forKey: .leftHeadline)
|
||||||
try container.encode(leftBody, forKey: .leftBody)
|
try container.encode(leftBody, forKey: .leftBody)
|
||||||
try container.encode(leftSubBody, forKey: .leftSubBody)
|
try container.encode(leftSubBody, forKey: .leftSubBody)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import Foundation
|
|||||||
let subHeadline = Label(fontStyle: .BoldBodySmall)
|
let subHeadline = Label(fontStyle: .BoldBodySmall)
|
||||||
let body = Label(fontStyle: .RegularBodySmall)
|
let body = Label(fontStyle: .RegularBodySmall)
|
||||||
let link = Link()
|
let link = Link()
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -52,6 +53,7 @@ import Foundation
|
|||||||
|
|
||||||
stack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.subHeadline, model.body, model.link],
|
stack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.subHeadline, model.body, model.link],
|
||||||
delegateObject, additionalData)
|
delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +105,7 @@ import Foundation
|
|||||||
|
|
||||||
func updateAccessibilityLabel() {
|
func updateAccessibilityLabel() {
|
||||||
|
|
||||||
let linkShowing = link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListOneColumnFullWidthTextAllTextAndLinksModel)?.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
if !linkShowing {
|
if !linkShowing {
|
||||||
// Make whole cell focusable if no link.
|
// Make whole cell focusable if no link.
|
||||||
|
|||||||
@ -32,7 +32,7 @@ public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, Molecul
|
|||||||
|
|
||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
self.button.size = .tiny
|
self.button.size = .small
|
||||||
self.button.style = .secondary
|
self.button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
private let stack: Stack<StackModel>
|
private let stack: Stack<StackModel>
|
||||||
private let arrowStackItem: StackItem
|
private let arrowStackItem: StackItem
|
||||||
private let rightLabelStackItem: StackItem
|
private let rightLabelStackItem: StackItem
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -73,6 +74,7 @@
|
|||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
|
rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
|
||||||
arrow.set(with: model.arrow, delegateObject, additionalData)
|
arrow.set(with: model.arrow, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +100,7 @@
|
|||||||
|
|
||||||
func updateAccessibilityLabel() {
|
func updateAccessibilityLabel() {
|
||||||
|
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListRightVariablePriceChangeAllTextAndLinksModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
||||||
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
||||||
public var stack: Stack<StackModel>
|
public var stack: Stack<StackModel>
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -65,6 +66,7 @@
|
|||||||
guard let model = model as? ListRightVariableRightCaretAllTextAndLinksModel else { return }
|
guard let model = model as? ListRightVariableRightCaretAllTextAndLinksModel else { return }
|
||||||
rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
|
rightLabel.set(with: model.rightLabel, delegateObject, additionalData)
|
||||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||||
|
self.model = model
|
||||||
updateAccessibilityLabel()
|
updateAccessibilityLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +97,7 @@
|
|||||||
|
|
||||||
func updateAccessibilityLabel() {
|
func updateAccessibilityLabel() {
|
||||||
|
|
||||||
let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0
|
let linkShowing = (model as? ListRightVariableRightCaretAllTextAndLinksModel)?.eyebrowHeadlineBodyLink.link?.title != nil
|
||||||
isAccessibilityElement = !linkShowing
|
isAccessibilityElement = !linkShowing
|
||||||
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
accessibilityTraits = (listItemModel?.accessibilityTraits) ?? ((isAccessibilityElement && accessoryView != nil) ? .button : .none)
|
||||||
|
|
||||||
|
|||||||
@ -6,15 +6,26 @@
|
|||||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
open class DataLine: View {
|
||||||
|
|
||||||
|
lazy var heightConstraint = heightAnchor.constraint(equalToConstant: 4)
|
||||||
|
lazy var widthConstraint = widthAnchor.constraint(equalToConstant: 20)
|
||||||
|
|
||||||
|
open override func setupView() {
|
||||||
|
super.setupView()
|
||||||
|
heightConstraint.isActive = true
|
||||||
|
widthConstraint.isActive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objcMembers open class ListRightVariableTotalData: TableViewCell {
|
open class ListRightVariableTotalData: TableViewCell {
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
// MARK: - Outlets
|
// MARK: - Outlets
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
|
|
||||||
public let leftLabel = Label(fontStyle: .BoldBodySmall)
|
public let leftLabel = Label(fontStyle: .BoldBodySmall)
|
||||||
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
public let rightLabel = Label(fontStyle: .RegularBodySmall)
|
||||||
public let bar = Line()
|
public let bar = DataLine()
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
@ -44,8 +55,6 @@
|
|||||||
|
|
||||||
override open func setupView() {
|
override open func setupView() {
|
||||||
super.setupView()
|
super.setupView()
|
||||||
bar.setStyle(.heavy)
|
|
||||||
bar.widthAnchor.constraint(equalToConstant: 20).isActive = true
|
|
||||||
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
|
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
|
||||||
addMolecule(stack)
|
addMolecule(stack)
|
||||||
stack.restack()
|
stack.restack()
|
||||||
@ -74,7 +83,6 @@
|
|||||||
super.reset()
|
super.reset()
|
||||||
leftLabel.setFontStyle(.BoldBodySmall)
|
leftLabel.setFontStyle(.BoldBodySmall)
|
||||||
rightLabel.setFontStyle(.RegularBodySmall)
|
rightLabel.setFontStyle(.RegularBodySmall)
|
||||||
bar.setStyle(.heavy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -6,8 +6,18 @@
|
|||||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
public struct DataLineModel: Codable, MoleculeModelProtocol {
|
||||||
|
public var id: String = UUID().uuidString
|
||||||
|
public static var identifier: String = "line"
|
||||||
|
public var backgroundColor: Color?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case backgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class ListRightVariableTotalDataModel: ListItemModel, MoleculeModelProtocol {
|
public class ListRightVariableTotalDataModel: ListItemModel, MoleculeModelProtocol {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -15,7 +25,7 @@ public class ListRightVariableTotalDataModel: ListItemModel, MoleculeModelProtoc
|
|||||||
public static var identifier: String = "listRVLine"
|
public static var identifier: String = "listRVLine"
|
||||||
public var leftLabel: LabelModel
|
public var leftLabel: LabelModel
|
||||||
public var rightLabel: LabelModel
|
public var rightLabel: LabelModel
|
||||||
public var bar: LineModel
|
public var bar: DataLineModel
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Method
|
// MARK: - Method
|
||||||
@ -24,8 +34,6 @@ public class ListRightVariableTotalDataModel: ListItemModel, MoleculeModelProtoc
|
|||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
rightLabel.hero = 0
|
rightLabel.hero = 0
|
||||||
bar.type = .heavy
|
|
||||||
|
|
||||||
if bar.backgroundColor == nil {
|
if bar.backgroundColor == nil {
|
||||||
bar.backgroundColor = Color(uiColor: .mvmBlue)
|
bar.backgroundColor = Color(uiColor: .mvmBlue)
|
||||||
}
|
}
|
||||||
@ -35,7 +43,7 @@ public class ListRightVariableTotalDataModel: ListItemModel, MoleculeModelProtoc
|
|||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public init(leftLabel: LabelModel, rightlabel:LabelModel, bar: LineModel){
|
public init(leftLabel: LabelModel, rightlabel:LabelModel, bar: DataLineModel) {
|
||||||
self.leftLabel = leftLabel
|
self.leftLabel = leftLabel
|
||||||
self.rightLabel = rightlabel
|
self.rightLabel = rightlabel
|
||||||
self.bar = bar
|
self.bar = bar
|
||||||
@ -46,7 +54,7 @@ public class ListRightVariableTotalDataModel: ListItemModel, MoleculeModelProtoc
|
|||||||
// MARK: - Keys
|
// MARK: - Keys
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey{
|
private enum CodingKeys: String, CodingKey {
|
||||||
case moleculeName
|
case moleculeName
|
||||||
case leftLabel
|
case leftLabel
|
||||||
case rightLabel
|
case rightLabel
|
||||||
@ -61,7 +69,7 @@ public class ListRightVariableTotalDataModel: ListItemModel, MoleculeModelProtoc
|
|||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel)
|
leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel)
|
||||||
rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel)
|
rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel)
|
||||||
bar = try typeContainer.decode(LineModel.self, forKey: .bar)
|
bar = try typeContainer.decode(DataLineModel.self, forKey: .bar)
|
||||||
try super.init(from: decoder)
|
try super.init(from: decoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,5 +24,14 @@
|
|||||||
if bottomPadding == nil {
|
if bottomPadding == nil {
|
||||||
bottomPadding = PaddingDefaultVerticalSpacing
|
bottomPadding = PaddingDefaultVerticalSpacing
|
||||||
}
|
}
|
||||||
|
guard !MVMCoreGetterUtility.isOnIPad(),
|
||||||
|
horizontalAlignment == nil else { return }
|
||||||
|
|
||||||
|
if let _ = molecule as? ButtonModel {
|
||||||
|
horizontalAlignment = .fill
|
||||||
|
} else if let model = molecule as? TwoButtonViewModel,
|
||||||
|
model.primaryButton == nil || model.secondaryButton == nil {
|
||||||
|
horizontalAlignment = .fill
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,13 @@ import VDSColorTokens
|
|||||||
|
|
||||||
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {
|
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {
|
||||||
|
|
||||||
public var model: TabBarModel
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
|
public var tabModel: TabBarModel {
|
||||||
|
get { model as! TabBarModel }
|
||||||
|
set { model = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
public var delegateObject: MVMCoreUIDelegateObject?
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
public let line = Line()
|
public let line = Line()
|
||||||
|
|
||||||
@ -50,7 +56,7 @@ import VDSColorTokens
|
|||||||
setItems(tabs, animated: false)
|
setItems(tabs, animated: false)
|
||||||
selectedItem = tabs[model.selectedTab]
|
selectedItem = tabs[model.selectedTab]
|
||||||
|
|
||||||
guard let lineModel = line.lineModel else { return }
|
guard let lineModel = line.viewModel else { return }
|
||||||
lineModel.inverted = model.style == .dark
|
lineModel.inverted = model.style == .dark
|
||||||
line.set(with: lineModel, delegateObject, additionalData)
|
line.set(with: lineModel, delegateObject, additionalData)
|
||||||
}
|
}
|
||||||
@ -68,31 +74,35 @@ import VDSColorTokens
|
|||||||
|
|
||||||
// MARK: - UITabBarDelegate
|
// MARK: - UITabBarDelegate
|
||||||
public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
||||||
model.selectedTab = item.tag
|
tabModel.selectedTab = item.tag
|
||||||
let action = model.tabs[item.tag].action
|
let action = tabModel.tabs[item.tag].action
|
||||||
Task(priority: .userInitiated) {
|
Task(priority: .userInitiated) {
|
||||||
try await Button.performButtonAction(with: action, button: item, delegateObject: delegateObject, additionalData: nil)
|
try await Button.performButtonAction(with: action, button: item, delegateObject: delegateObject, additionalData: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - TabBarProtocol
|
// MARK: - TabBarProtocol
|
||||||
|
@MainActor
|
||||||
public func highlightTab(at index: Int) {
|
public func highlightTab(at index: Int) {
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
guard let items = items, index >= 0, index < items.count else {
|
||||||
guard let newSelectedItem = self.items?[index] else { return }
|
MVMCoreLoggingHandler.shared()?.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Invalid tab index \(index). \(items?.count ?? 0) tabs available .", code: 0, domain: ErrorDomainSystem, location: #function)!)
|
||||||
self.model.selectedTab = index
|
return
|
||||||
self.selectedItem = newSelectedItem
|
}
|
||||||
})
|
tabModel.selectedTab = index
|
||||||
|
selectedItem = items[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
public func selectTab(at index: Int) {
|
public func selectTab(at index: Int) {
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
guard let items = items, index >= 0, index < items.count else {
|
||||||
guard let newSelectedItem = self.items?[index] else { return }
|
MVMCoreLoggingHandler.shared()?.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Invalid tab index \(index). \(items?.count ?? 0) tabs available.", code: 0, domain: ErrorDomainSystem, location: #function)!)
|
||||||
self.selectedItem = newSelectedItem
|
return
|
||||||
self.tabBar(self, didSelect: newSelectedItem)
|
}
|
||||||
})
|
selectedItem = items[index]
|
||||||
|
tabBar(self, didSelect: items[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func currentTabIndex() -> Int { model.selectedTab }
|
public func currentTabIndex() -> Int { tabModel.selectedTab }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UITabBarItem: MFButtonProtocol { }
|
extension UITabBarItem: MFButtonProtocol { }
|
||||||
|
|||||||
@ -174,7 +174,7 @@ import VDSColorTokens
|
|||||||
self.additionalData = additionalData
|
self.additionalData = additionalData
|
||||||
selectedIndex = tabsModel?.selectedIndex ?? 0
|
selectedIndex = tabsModel?.selectedIndex ?? 0
|
||||||
selectionLine.backgroundColor = tabsModel?.selectedBarColor.uiColor
|
selectionLine.backgroundColor = tabsModel?.selectedBarColor.uiColor
|
||||||
let lineModel = bottomLine.lineModel ?? LineModel(type: .secondary)
|
let lineModel = bottomLine.viewModel ?? LineModel(type: .secondary)
|
||||||
lineModel.inverted = tabsModel?.style == .dark
|
lineModel.inverted = tabsModel?.style == .dark
|
||||||
bottomLine.set(with: lineModel, delegateObject, additionalData)
|
bottomLine.set(with: lineModel, delegateObject, additionalData)
|
||||||
reloadData()
|
reloadData()
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import VDS
|
||||||
|
|
||||||
@objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol {
|
@objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -29,9 +29,8 @@ import UIKit
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public func setDefaultAppearance() {
|
public func setDefaultAppearance() {
|
||||||
|
primaryButton.use = .primary
|
||||||
primaryButton.stylePrimary()
|
secondaryButton.use = .secondary
|
||||||
secondaryButton.styleSecondary()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open override func updateView(_ size: CGFloat) {
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import VDS
|
||||||
|
|
||||||
public class TwoButtonViewModel: ParentMoleculeModelProtocol {
|
public class TwoButtonViewModel: ParentMoleculeModelProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -56,13 +56,13 @@ public class TwoButtonViewModel: ParentMoleculeModelProtocol {
|
|||||||
|
|
||||||
//set context value for 'primary' style to be set for the primaryButton in case the
|
//set context value for 'primary' style to be set for the primaryButton in case the
|
||||||
//property is not returned in the JSON and once decoded, this value is removed from the context
|
//property is not returned in the JSON and once decoded, this value is removed from the context
|
||||||
try decoder.setContext(value: Styler.Button.Style.primary, for: "style") {
|
try decoder.setContext(value: Use.primary, for: "style") {
|
||||||
self.primaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .primaryButton)
|
self.primaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .primaryButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
//set context value for 'secondary' style to be set for the primaryButton in case the
|
//set context value for 'secondary' style to be set for the primaryButton in case the
|
||||||
//property is not returned in the JSON and once decoded, this value is removed from the context
|
//property is not returned in the JSON and once decoded, this value is removed from the context
|
||||||
try decoder.setContext(value: Styler.Button.Style.secondary, for: "style") {
|
try decoder.setContext(value: Use.secondary, for: "style") {
|
||||||
self.secondaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .secondaryButton)
|
self.secondaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .secondaryButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,9 +19,8 @@
|
|||||||
public var line: LineModel?
|
public var line: LineModel?
|
||||||
public var style: ListItemStyle?
|
public var style: ListItemStyle?
|
||||||
public var accessibilityTraits: UIAccessibilityTraits?
|
public var accessibilityTraits: UIAccessibilityTraits?
|
||||||
public var accessibilityText: String?
|
|
||||||
public var accessibilityValue: String?
|
public var accessibilityValue: String?
|
||||||
|
public var accessibilityText: String?
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Keys
|
// MARK: - Keys
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -33,8 +32,8 @@
|
|||||||
case line
|
case line
|
||||||
case style
|
case style
|
||||||
case accessibilityTraits
|
case accessibilityTraits
|
||||||
case accessibilityText
|
|
||||||
case accessibilityValue
|
case accessibilityValue
|
||||||
|
case accessibilityText
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -109,7 +108,6 @@
|
|||||||
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line)
|
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line)
|
||||||
style = try typeContainer.decodeIfPresent(ListItemStyle.self, forKey: .style)
|
style = try typeContainer.decodeIfPresent(ListItemStyle.self, forKey: .style)
|
||||||
accessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .accessibilityTraits)
|
accessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .accessibilityTraits)
|
||||||
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
|
|
||||||
accessibilityValue = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityValue)
|
accessibilityValue = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityValue)
|
||||||
try super.init(from: decoder)
|
try super.init(from: decoder)
|
||||||
}
|
}
|
||||||
@ -123,7 +121,7 @@
|
|||||||
try container.encodeIfPresent(line, forKey: .line)
|
try container.encodeIfPresent(line, forKey: .line)
|
||||||
try container.encodeIfPresent(style, forKey: .style)
|
try container.encodeIfPresent(style, forKey: .style)
|
||||||
try container.encodeIfPresent(accessibilityTraits, forKey: .accessibilityTraits)
|
try container.encodeIfPresent(accessibilityTraits, forKey: .accessibilityTraits)
|
||||||
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
|
|
||||||
try container.encodeIfPresent(accessibilityValue, forKey: .accessibilityValue)
|
try container.encodeIfPresent(accessibilityValue, forKey: .accessibilityValue)
|
||||||
|
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,13 +9,15 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
open class HeadlineBodyToggleModel: MoleculeModelProtocol {
|
open class HeadlineBodyToggleModel: MoleculeModelProtocol, ParentMoleculeModelProtocol {
|
||||||
public static var identifier: String = "headlineBodyToggle"
|
public static var identifier: String = "headlineBodyToggle"
|
||||||
public var moleculeName: String = HeadlineBodyToggleModel.identifier
|
public var moleculeName: String = HeadlineBodyToggleModel.identifier
|
||||||
@DecodableDefault.UUIDString public var id: String
|
@DecodableDefault.UUIDString public var id: String
|
||||||
open var backgroundColor: Color?
|
open var backgroundColor: Color?
|
||||||
open var headlineBody: HeadlineBodyModel
|
open var headlineBody: HeadlineBodyModel
|
||||||
open var toggle: ToggleModel
|
open var toggle: ToggleModel
|
||||||
|
|
||||||
|
public var children: [MoleculeModelProtocol] { [headlineBody, toggle] }
|
||||||
|
|
||||||
public init(_ headlineBody: HeadlineBodyModel, _ toggle: ToggleModel) {
|
public init(_ headlineBody: HeadlineBodyModel, _ toggle: ToggleModel) {
|
||||||
self.headlineBody = headlineBody
|
self.headlineBody = headlineBody
|
||||||
|
|||||||
@ -75,7 +75,7 @@ open class ModuleMolecule: Container {
|
|||||||
let _ = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
|
let _ = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
|
||||||
if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) {
|
if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) {
|
||||||
error?.pointee = errorObject
|
error?.pointee = errorObject
|
||||||
MVMCoreUILoggingHandler.addError(toLog: errorObject)
|
MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,15 +89,13 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button?.size = .tiny
|
button?.size = .small
|
||||||
button?.style = .secondary
|
button?.style = .secondary
|
||||||
switch style {
|
switch style {
|
||||||
case .error, .warning:
|
case .error, .warning:
|
||||||
button?.enabledTextColor = Color(uiColor: .mvmBlack)
|
button?.inverted = false
|
||||||
button?.enabledBorderColor = Color(uiColor: .mvmBlack)
|
|
||||||
default:
|
default:
|
||||||
button?.enabledTextColor = Color(uiColor: .mvmWhite)
|
button?.inverted = true
|
||||||
button?.enabledBorderColor = Color(uiColor: .mvmWhite)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if closeButton?.color == nil {
|
if closeButton?.color == nil {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import Foundation
|
|||||||
|
|
||||||
public let headline = Label(fontStyle: .BoldBodySmall)
|
public let headline = Label(fontStyle: .BoldBodySmall)
|
||||||
public let body = Label(fontStyle: .RegularBodySmall)
|
public let body = Label(fontStyle: .RegularBodySmall)
|
||||||
public let button = PillButton(asPrimaryButton: false, makeTiny: true)
|
public let button = PillButton()
|
||||||
public let closeButton = NotificationXButton()
|
public let closeButton = NotificationXButton()
|
||||||
public var labelStack: Stack<StackModel>!
|
public var labelStack: Stack<StackModel>!
|
||||||
public var horizontalStack: Stack<StackModel>!
|
public var horizontalStack: Stack<StackModel>!
|
||||||
|
|||||||
@ -54,7 +54,7 @@ public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtoc
|
|||||||
image.height = BGImageHeadlineBodyButton.heightConstant
|
image.height = BGImageHeadlineBodyButton.heightConstant
|
||||||
}
|
}
|
||||||
|
|
||||||
button?.size = .tiny
|
button?.size = .small
|
||||||
button?.style = .secondary
|
button?.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -88,15 +88,15 @@
|
|||||||
|
|
||||||
var message = ""
|
var message = ""
|
||||||
|
|
||||||
if let eyebrowLabel = eyebrow.text {
|
if let eyebrowLabel = eyebrow.accessibilityLabel ?? eyebrow.text {
|
||||||
message += eyebrowLabel + ", "
|
message += eyebrowLabel + ", "
|
||||||
}
|
}
|
||||||
|
|
||||||
if let headlineLabel = headline.text {
|
if let headlineLabel = headline.accessibilityLabel ?? headline.text {
|
||||||
message += headlineLabel + ", "
|
message += headlineLabel + ", "
|
||||||
}
|
}
|
||||||
|
|
||||||
if let bodyLabel = body.text {
|
if let bodyLabel = body.accessibilityLabel ?? body.text {
|
||||||
message += bodyLabel
|
message += bodyLabel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
headlineBody.headlineLabel.font = Styler.Font.BoldTitleMedium.getFont()
|
headlineBody.headlineLabel.font = Styler.Font.BoldTitleMedium.getFont()
|
||||||
headlineBody.messageLabel.font = Styler.Font.RegularMicro.getFont()
|
headlineBody.messageLabel.font = Styler.Font.RegularMicro.getFont()
|
||||||
button.styleSecondary()
|
button.use = .secondary
|
||||||
button.isHidden = false
|
button.isHidden = false
|
||||||
buttonHeadlinePadding = PaddingTwo
|
buttonHeadlinePadding = PaddingTwo
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public class HeadlineBodyButtonModel: MoleculeModelProtocol {
|
|||||||
|
|
||||||
/// Defaults to set
|
/// Defaults to set
|
||||||
public func setDefaults() {
|
public func setDefaults() {
|
||||||
button.size = .tiny
|
button.size = .small
|
||||||
button.style = .secondary
|
button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -87,7 +87,7 @@ open class Carousel: View {
|
|||||||
showPeaking(false)
|
showPeaking(false)
|
||||||
|
|
||||||
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
|
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
|
||||||
guard let model = model as? CarouselModel,
|
guard let model = model as? CarouselModel, !model.molecules.isEmpty,
|
||||||
(model.paging == true || loop == true) else { return }
|
(model.paging == true || loop == true) else { return }
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false)
|
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false)
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
@objcMembers public class CarouselModel: ParentMoleculeModelProtocol, FormFieldProtocol {
|
@objcMembers public class CarouselModel: ParentMoleculeModelProtocol, FormFieldProtocol {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -57,14 +56,14 @@ import UIKit
|
|||||||
|
|
||||||
guard selectable else {
|
guard selectable else {
|
||||||
// Use visible item value, else index
|
// Use visible item value, else index
|
||||||
if let fieldValue = molecules[index].formFieldValue() {
|
if let fieldValue = molecules[safe: index]?.formFieldValue() {
|
||||||
return fieldValue
|
return fieldValue
|
||||||
}
|
}
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
// Use selected item value, else index
|
// Use selected item value, else index
|
||||||
guard let selectedIndex = selectedIndex else { return nil }
|
guard let selectedIndex = selectedIndex else { return nil }
|
||||||
guard let fieldValue = molecules[selectedIndex].formFieldValue() else { return selectedIndex }
|
guard let fieldValue = molecules[safe: selectedIndex]?.formFieldValue() else { return selectedIndex }
|
||||||
return fieldValue
|
return fieldValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,13 +48,6 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
|
|||||||
}
|
}
|
||||||
|
|
||||||
isAccessibilityElement = false
|
isAccessibilityElement = false
|
||||||
var accessibleViews: [Any] = []
|
|
||||||
|
|
||||||
for (index, view) in stackItems.enumerated() where !stackModel.molecules[index].gone {
|
|
||||||
accessibleViews.append(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
accessibilityElements = accessibleViews
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all stack items views from the view.
|
/// Removes all stack items views from the view.
|
||||||
|
|||||||
@ -37,3 +37,9 @@ public extension MVMCoreUIDelegateObject {
|
|||||||
return (moleculeDelegate as? MoleculeListProtocol & NSObjectProtocol)
|
return (moleculeDelegate as? MoleculeListProtocol & NSObjectProtocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol MoleculeCollectionListProtocol {
|
||||||
|
|
||||||
|
/// Asks the delegate for the index of molecule.
|
||||||
|
func getIndexPath(for molecule: CollectionItemModelProtocol & MoleculeModelProtocol) -> IndexPath?
|
||||||
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import MVMCore.MVMCoreViewProtocol
|
|||||||
|
|
||||||
public protocol MoleculeViewProtocol: UIView, ModelHandlerProtocol {
|
public protocol MoleculeViewProtocol: UIView, ModelHandlerProtocol {
|
||||||
|
|
||||||
|
var model: MoleculeModelProtocol? { get set }
|
||||||
|
|
||||||
/// Initializes the view with the model
|
/// Initializes the view with the model
|
||||||
init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?)
|
init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?)
|
||||||
|
|
||||||
@ -33,6 +35,11 @@ public protocol MoleculeViewProtocol: UIView, ModelHandlerProtocol {
|
|||||||
|
|
||||||
extension MoleculeViewProtocol {
|
extension MoleculeViewProtocol {
|
||||||
|
|
||||||
|
public var model: MoleculeModelProtocol? {
|
||||||
|
get { nil }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
/// Calls set with model
|
/// Calls set with model
|
||||||
public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
self.init(frame: .zero)
|
self.init(frame: .zero)
|
||||||
@ -70,7 +77,7 @@ public extension ModelRegistry {
|
|||||||
return type
|
return type
|
||||||
} catch {
|
} catch {
|
||||||
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
|
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
|
||||||
MVMCoreLoggingHandler.addError(toLog: errorObject)
|
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,10 +13,10 @@ import Foundation
|
|||||||
var delegateObject: MVMCoreUIDelegateObject? { get set }
|
var delegateObject: MVMCoreUIDelegateObject? { get set }
|
||||||
|
|
||||||
/// Should visually select the given tab index.
|
/// Should visually select the given tab index.
|
||||||
@objc func highlightTab(at index: Int)
|
@MainActor func highlightTab(at index: Int)
|
||||||
|
|
||||||
/// Should select the tab index. As if the user selected it.
|
/// Should select the tab index. As if the user selected it.
|
||||||
@objc func selectTab(at index: Int)
|
@MainActor func selectTab(at index: Int)
|
||||||
|
|
||||||
/// Returns the current tab
|
/// Returns the current tab
|
||||||
@objc func currentTabIndex() -> Int
|
@objc func currentTabIndex() -> Int
|
||||||
|
|||||||
18
MVMCoreUI/Atomic/Protocols/VDS-Interpreters.swift
Normal file
18
MVMCoreUI/Atomic/Protocols/VDS-Interpreters.swift
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// VDS-Interpreters.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 9/20/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import VDS
|
||||||
|
|
||||||
|
public protocol Invertable {
|
||||||
|
var inverted: Bool { get set }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Invertable {
|
||||||
|
public var surface: Surface { return inverted ? .dark : .light }
|
||||||
|
}
|
||||||
@ -28,5 +28,9 @@ extension VDSMoleculeViewProtocol {
|
|||||||
viewModel = castedModel
|
viewModel = castedModel
|
||||||
viewModelDidUpdate()
|
viewModelDidUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func update(viewModel: ViewModel){
|
||||||
|
set(with: viewModel, delegateObject, additionalData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -198,3 +198,11 @@
|
|||||||
return modules
|
return modules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension CollectionTemplate: MoleculeCollectionListProtocol {
|
||||||
|
|
||||||
|
public func getIndexPath(for molecule: CollectionItemModelProtocol & MoleculeModelProtocol) -> IndexPath? {
|
||||||
|
guard let index = (moleculesInfo?.firstIndex { $0.molecule.id == molecule.id }) else { return nil }
|
||||||
|
return IndexPath(item: index, section: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -35,9 +35,4 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate {
|
|||||||
MVMCoreUIActionHandler.performActionUnstructured(with: closeAction, additionalData: nil, delegateObject: self.delegateObject())
|
MVMCoreUIActionHandler.performActionUnstructured(with: closeAction, additionalData: nil, delegateObject: self.delegateObject())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func viewDidLayoutSubviews() {
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
accessibilityElements = [closeButton as Any, tableView as Any]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -288,14 +288,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
|||||||
|
|
||||||
/// Checks if the two molecules are equal
|
/// Checks if the two molecules are equal
|
||||||
private func equal(moleculeA: MoleculeModelProtocol, moleculeB: MoleculeModelProtocol) -> Bool {
|
private func equal(moleculeA: MoleculeModelProtocol, moleculeB: MoleculeModelProtocol) -> Bool {
|
||||||
// TODO: move this to a better approach, maybe a UUID for each model.
|
moleculeA.id == moleculeB.id
|
||||||
// Do instance check
|
|
||||||
if let classMoleculeA = moleculeA as? NSObjectProtocol,
|
|
||||||
let classMoleculeB = moleculeB as? NSObjectProtocol {
|
|
||||||
return classMoleculeA === classMoleculeB
|
|
||||||
}
|
|
||||||
// Do json check
|
|
||||||
return moleculeA.toJSON() == moleculeB.toJSON()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,11 +12,13 @@
|
|||||||
#import "UIColor+MFConvenience.h"
|
#import "UIColor+MFConvenience.h"
|
||||||
#import "MFStyler.h"
|
#import "MFStyler.h"
|
||||||
#import "MVMCoreUICommonViewsUtility.h"
|
#import "MVMCoreUICommonViewsUtility.h"
|
||||||
|
#import <MVMCoreUI/MVMCoreUI-Swift.h>
|
||||||
|
|
||||||
@interface MFLoadingViewController ()
|
@interface MFLoadingViewController ()
|
||||||
|
|
||||||
@property (nullable, weak, nonatomic) MFLoadingSpinner *activityIndicator;
|
@property (nullable, weak, nonatomic) MFLoadingSpinner *activityIndicator;
|
||||||
@property (nullable, weak, nonatomic) UIView *transparentBackgroundView;
|
@property (nullable, weak, nonatomic) UIView *transparentBackgroundView;
|
||||||
|
@property (nullable, weak, nonatomic) Label *indicatorText;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -27,23 +29,41 @@
|
|||||||
view.backgroundColor = [UIColor clearColor];
|
view.backgroundColor = [UIColor clearColor];
|
||||||
self.view = view;
|
self.view = view;
|
||||||
|
|
||||||
|
UIStackView *loadingStack = [[UIStackView alloc] initWithFrame:CGRectZero];
|
||||||
|
loadingStack.axis = UILayoutConstraintAxisVertical;
|
||||||
|
loadingStack.alignment = UIStackViewAlignmentCenter;
|
||||||
|
loadingStack.spacing = 20;
|
||||||
|
|
||||||
// Sets up the loading view.
|
// Sets up the loading view.
|
||||||
MFLoadingSpinner *activityIndicatorView = [[MFLoadingSpinner alloc] initWithFrame:CGRectMake(0, 0, 36, 36)];
|
MFLoadingSpinner *activityIndicatorView = [[MFLoadingSpinner alloc] initWithFrame:CGRectMake(0, 0, 36, 36)];
|
||||||
activityIndicatorView.backgroundColor = [UIColor clearColor];
|
activityIndicatorView.backgroundColor = [UIColor clearColor];
|
||||||
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = NO;
|
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||||
[view addSubview:activityIndicatorView];
|
|
||||||
self.activityIndicator = activityIndicatorView;
|
self.activityIndicator = activityIndicatorView;
|
||||||
self.activityIndicator.accessibilityIdentifier = @"Loader";
|
self.activityIndicator.accessibilityIdentifier = @"Loader";
|
||||||
[activityIndicatorView pinWidthAndHeight];
|
[activityIndicatorView pinWidthAndHeight];
|
||||||
|
|
||||||
|
Label *infoLabel = [Label label];
|
||||||
|
infoLabel.textAlignment = NSTextAlignmentCenter;
|
||||||
|
infoLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||||
|
infoLabel.hidden = true;
|
||||||
|
self.indicatorText = infoLabel;
|
||||||
|
|
||||||
|
[loadingStack addArrangedSubview:infoLabel];
|
||||||
|
[loadingStack addArrangedSubview:activityIndicatorView];
|
||||||
|
|
||||||
|
loadingStack.translatesAutoresizingMaskIntoConstraints = NO;
|
||||||
|
[view addSubview:loadingStack];
|
||||||
// Sets the constraints for the activityIndicatorView
|
// Sets the constraints for the activityIndicatorView
|
||||||
[NSLayoutConstraint constraintPinSubview:activityIndicatorView pinCenterX:YES pinCenterY:YES];
|
|
||||||
|
[NSLayoutConstraint constraintWithItem:infoLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:1.0];
|
||||||
|
[NSLayoutConstraint constraintPinSubview:loadingStack pinCenterX:YES pinCenterY:YES];
|
||||||
|
[NSLayoutConstraint constraintPinSubview:loadingStack pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
|
||||||
|
|
||||||
// Sets up the transparent background view.
|
// Sets up the transparent background view.
|
||||||
UIView *transparentBackground = [MVMCoreUICommonViewsUtility commonView];
|
UIView *transparentBackground = [MVMCoreUICommonViewsUtility commonView];
|
||||||
transparentBackground.backgroundColor = [UIColor mfBackgroundGray];
|
transparentBackground.backgroundColor = [UIColor mfBackgroundGray];
|
||||||
transparentBackground.alpha = 0.9;
|
transparentBackground.alpha = 0.9;
|
||||||
[view insertSubview:transparentBackground belowSubview:activityIndicatorView];
|
[view insertSubview:transparentBackground belowSubview:loadingStack];
|
||||||
self.transparentBackgroundView = transparentBackground;
|
self.transparentBackgroundView = transparentBackground;
|
||||||
|
|
||||||
// Sets the constraints of the transparent background view to be the same as the activity indicator view.
|
// Sets the constraints of the transparent background view to be the same as the activity indicator view.
|
||||||
@ -61,8 +81,24 @@
|
|||||||
[self.activityIndicator resumeSpinner];
|
[self.activityIndicator resumeSpinner];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)startLoadingWith:(nullable NSAttributedString *) text{
|
||||||
|
if(text != nil){
|
||||||
|
self.indicatorText.attributedText = text;
|
||||||
|
self.indicatorText.accessibilityLabel = text.string;
|
||||||
|
self.indicatorText.hidden = false;
|
||||||
|
} else {
|
||||||
|
self.indicatorText.attributedText = nil;
|
||||||
|
self.indicatorText.accessibilityLabel = @"";
|
||||||
|
self.indicatorText.hidden = true;
|
||||||
|
}
|
||||||
|
[self.activityIndicator resumeSpinner];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)stopLoading {
|
- (void)stopLoading {
|
||||||
[self.activityIndicator pauseSpinner];
|
[self.activityIndicator pauseSpinner];
|
||||||
|
self.indicatorText.hidden = true;
|
||||||
|
self.indicatorText.attributedText = nil;
|
||||||
|
self.indicatorText.accessibilityLabel = @"";
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -9,10 +9,14 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// A view controller that has three main layers, a header, collection rows, and a footer. The header is added as a supplement header to the first section, and the footer is added as a supplement footer to the last section. This view controller allows for flexible space between the three layers to fit the screeen.
|
/// A view controller that has three main layers, a header, collection rows, and a footer. The header is added as a supplement header to the first section, and the footer is added as a supplement footer to the last section. This view controller allows for flexible space between the three layers to fit the screeen.
|
||||||
@objc open class ThreeLayerCollectionViewController: ProgrammaticCollectionViewController, UICollectionViewDelegateFlowLayout {
|
@objc open class ThreeLayerCollectionViewController: ProgrammaticCollectionViewController, UICollectionViewDelegateFlowLayout, RotorViewElementsProtocol {
|
||||||
|
|
||||||
private var topView: UIView?
|
public var topView: UIView?
|
||||||
private var bottomView: UIView?
|
public var middleView: UIView? {
|
||||||
|
set {}
|
||||||
|
get { collectionView }
|
||||||
|
}
|
||||||
|
public var bottomView: UIView?
|
||||||
private var headerView: ContainerCollectionReusableView?
|
private var headerView: ContainerCollectionReusableView?
|
||||||
private var footerView: ContainerCollectionReusableView?
|
private var footerView: ContainerCollectionReusableView?
|
||||||
private let headerID = "header"
|
private let headerID = "header"
|
||||||
|
|||||||
@ -8,15 +8,20 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
open class ThreeLayerTableViewController: ProgrammaticTableViewController {
|
open class ThreeLayerTableViewController: ProgrammaticTableViewController, RotorViewElementsProtocol {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Main Views
|
// MARK: - Main Views
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
private var topView: UIView?
|
|
||||||
private var bottomView: UIView?
|
|
||||||
private var headerView: UIView?
|
private var headerView: UIView?
|
||||||
private var footerView: UIView?
|
private var footerView: UIView?
|
||||||
|
public var topView: UIView?
|
||||||
|
public var middleView: UIView? {
|
||||||
|
get { tableView }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
public var bottomView: UIView?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
|||||||
@ -9,12 +9,12 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
open class ThreeLayerViewController: ProgrammaticScrollViewController {
|
open class ThreeLayerViewController: ProgrammaticScrollViewController, RotorViewElementsProtocol {
|
||||||
|
|
||||||
// The three main views
|
// The three main views
|
||||||
var topView: UIView?
|
public var topView: UIView?
|
||||||
var middleView: UIView?
|
public var middleView: UIView?
|
||||||
var bottomView: UIView?
|
public var bottomView: UIView?
|
||||||
var useMargins: Bool = false
|
var useMargins: Bool = false
|
||||||
|
|
||||||
// The top view can be put outside of the scrolling area.
|
// The top view can be put outside of the scrolling area.
|
||||||
|
|||||||
@ -115,7 +115,7 @@ import MVMCore
|
|||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") {
|
if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") {
|
||||||
MVMCoreLoggingHandler.addError(toLog: coreError)
|
MVMCoreLoggingHandler.shared()?.addError(toLog: coreError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public extension PageBehaviorHandlerProtocol {
|
|||||||
behaviors.append(behavior)
|
behaviors.append(behavior)
|
||||||
} catch {
|
} catch {
|
||||||
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
|
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
|
||||||
MVMCoreLoggingHandler.addError(toLog: errorObject)
|
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ import Combine
|
|||||||
|
|
||||||
extension NavigationController: MVMCoreViewManagerProtocol {
|
extension NavigationController: MVMCoreViewManagerProtocol {
|
||||||
public func getAccessibilityElements() -> [Any]? {
|
public func getAccessibilityElements() -> [Any]? {
|
||||||
return []
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getCurrentViewController() -> UIViewController? {
|
public func getCurrentViewController() -> UIViewController? {
|
||||||
|
|||||||
@ -82,13 +82,6 @@ public extension UINavigationController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a ShadowImage based on the line property of NavigationItemModelProtocol
|
|
||||||
func getNavigationBarShadowImage(for navigationItemModel: NavigationItemModelProtocol) -> UIImage? {
|
|
||||||
guard let thickness = navigationItemModel.line?.thickness,
|
|
||||||
let backgroundColor = navigationItemModel.line?.backgroundColor else { return nil }
|
|
||||||
return backgroundColor.uiColor.image(CGSize(width: thickness, height: thickness))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience function for setting the navigation bar ui
|
/// Convenience function for setting the navigation bar ui
|
||||||
@MainActor
|
@MainActor
|
||||||
func setNavigationBarUI(with model: NavigationItemModelProtocol) {
|
func setNavigationBarUI(with model: NavigationItemModelProtocol) {
|
||||||
@ -105,14 +98,7 @@ public extension UINavigationController {
|
|||||||
appearance.backgroundColor = backgroundColor
|
appearance.backgroundColor = backgroundColor
|
||||||
appearance.titleTextAttributes.updateValue(tint, forKey: .foregroundColor)
|
appearance.titleTextAttributes.updateValue(tint, forKey: .foregroundColor)
|
||||||
appearance.titlePositionAdjustment = model.titleOffset ?? .zero
|
appearance.titlePositionAdjustment = model.titleOffset ?? .zero
|
||||||
if let type = model.line?.type,
|
appearance.setShadow(for: model.line)
|
||||||
type != .none,
|
|
||||||
let color = model.line?.backgroundColor {
|
|
||||||
appearance.shadowColor = color.uiColor
|
|
||||||
} else {
|
|
||||||
appearance.shadowColor = .clear
|
|
||||||
}
|
|
||||||
appearance.shadowImage = getNavigationBarShadowImage(for: model)?.withRenderingMode(.alwaysTemplate)
|
|
||||||
navigationBar.standardAppearance = appearance
|
navigationBar.standardAppearance = appearance
|
||||||
navigationBar.scrollEdgeAppearance = appearance
|
navigationBar.scrollEdgeAppearance = appearance
|
||||||
|
|
||||||
@ -126,3 +112,16 @@ public extension UINavigationController {
|
|||||||
return viewController
|
return viewController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension UINavigationBarAppearance {
|
||||||
|
func setShadow(for model: LineModel?) {
|
||||||
|
let model = model ?? LineModel(type: .secondary)
|
||||||
|
let line = Line(model: model, nil, nil)
|
||||||
|
if line.shouldBeVisible() {
|
||||||
|
shadowColor = line.lineColor
|
||||||
|
} else {
|
||||||
|
shadowColor = .clear
|
||||||
|
}
|
||||||
|
shadowImage = line.lineColor.image(CGSize(width: line.lineWidth, height: line.lineWidth)).withRenderingMode(.alwaysTemplate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -250,7 +250,7 @@ public extension MVMCoreUISplitViewController {
|
|||||||
|
|
||||||
extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol {
|
extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol {
|
||||||
public func getAccessibilityElements() -> [Any]? {
|
public func getAccessibilityElements() -> [Any]? {
|
||||||
return []
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getCurrentViewController() -> UIViewController? {
|
public func getCurrentViewController() -> UIViewController? {
|
||||||
|
|||||||
@ -95,7 +95,7 @@ import MVMCore
|
|||||||
groupValid = try validateGroup(group)
|
groupValid = try validateGroup(group)
|
||||||
} catch {
|
} catch {
|
||||||
if let err = MVMCoreErrorObject.createErrorObject(for: error, location: "FormValidator"){
|
if let err = MVMCoreErrorObject.createErrorObject(for: error, location: "FormValidator"){
|
||||||
MVMCoreLoggingHandler.addError(toLog: err)
|
MVMCoreLoggingHandler.shared()?.addError(toLog: err)
|
||||||
fatalError(err.description)
|
fatalError(err.description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,6 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[];
|
|||||||
// In this header, you should import all the public headers of your framework using statements like #import <MVMCoreUI/PublicHeader.h>
|
// In this header, you should import all the public headers of your framework using statements like #import <MVMCoreUI/PublicHeader.h>
|
||||||
#pragma mark - OtherHandlers
|
#pragma mark - OtherHandlers
|
||||||
#import <MVMCoreUI/MVMCoreUISession.h>
|
#import <MVMCoreUI/MVMCoreUISession.h>
|
||||||
#import <MVMCoreUI/MVMCoreUILoggingHandler.h>
|
|
||||||
#import <MVMCoreUI/MVMCoreUIViewControllerMappingObject.h>
|
#import <MVMCoreUI/MVMCoreUIViewControllerMappingObject.h>
|
||||||
#import <MVMCoreUI/MVMCoreUIViewConstrainingProtocol.h>
|
#import <MVMCoreUI/MVMCoreUIViewConstrainingProtocol.h>
|
||||||
|
|
||||||
|
|||||||
@ -125,13 +125,14 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
|
|||||||
/// Hides/Shows the navigation bar for the page.
|
/// Hides/Shows the navigation bar for the page.
|
||||||
open func hideNavigationBarLine(_ isHidden: Bool) {
|
open func hideNavigationBarLine(_ isHidden: Bool) {
|
||||||
guard self == navigationController?.topViewController else { return }
|
guard self == navigationController?.topViewController else { return }
|
||||||
var color = UIColor.clear
|
var model: LineModel?
|
||||||
if !isHidden,
|
if isHidden {
|
||||||
let backgroundColor = (getCurrentViewController() as? PageProtocol)?.pageModel?.navigationBar?.line?.backgroundColor?.uiColor {
|
model = LineModel(type: .none)
|
||||||
color = backgroundColor
|
} else if let lineModel = (getCurrentViewController() as? PageProtocol)?.pageModel?.navigationBar?.line {
|
||||||
|
model = lineModel
|
||||||
}
|
}
|
||||||
navigationController?.navigationBar.standardAppearance.shadowColor = color
|
navigationController?.navigationBar.standardAppearance.setShadow(for: model)
|
||||||
navigationController?.navigationBar.scrollEdgeAppearance?.shadowColor = color
|
navigationController?.navigationBar.scrollEdgeAppearance?.setShadow(for: model)
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateViews() {
|
open override func updateViews() {
|
||||||
@ -307,6 +308,10 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc public func getAccessibilityElements() -> [Any]? {
|
||||||
|
[tabs]
|
||||||
|
}
|
||||||
|
|
||||||
open func newDataReceived(in viewController: UIViewController) {
|
open func newDataReceived(in viewController: UIViewController) {
|
||||||
manager?.newDataReceived?(in: viewController)
|
manager?.newDataReceived?(in: viewController)
|
||||||
hideNavigationBarLine(true)
|
hideNavigationBarLine(true)
|
||||||
|
|||||||
@ -25,16 +25,6 @@ public class NotificationContainerView: UIView {
|
|||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
setupView()
|
setupView()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Posts a layout change with taking the arguments from the view following the AccessibilityProtocol.
|
|
||||||
private func updateAccessibilityForTopAlert(_ view: UIView) {
|
|
||||||
// Update accessibility with top alert
|
|
||||||
var accessibilityArgument: Any? = view
|
|
||||||
if let view = view as? AccessibilityProtocol {
|
|
||||||
accessibilityArgument = view.getAccessibilityLayoutChangedArgument()
|
|
||||||
}
|
|
||||||
UIAccessibility.post(notification: .layoutChanged, argument: accessibilityArgument)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationContainerView: NotificationTransitionDelegateProtocol {
|
extension NotificationContainerView: NotificationTransitionDelegateProtocol {
|
||||||
@ -56,7 +46,6 @@ extension NotificationContainerView: NotificationTransitionDelegateProtocol {
|
|||||||
self.superview?.layoutIfNeeded()
|
self.superview?.layoutIfNeeded()
|
||||||
} completion: { finished in
|
} completion: { finished in
|
||||||
self.superview?.layoutIfNeeded()
|
self.superview?.layoutIfNeeded()
|
||||||
self.updateAccessibilityForTopAlert(notification)
|
|
||||||
continuation.resume()
|
continuation.resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,14 +53,11 @@ extension NotificationContainerView: NotificationTransitionDelegateProtocol {
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public func hide(notification: UIView) async {
|
public func hide(notification: UIView) async {
|
||||||
// accessibility - below line added to notify VI user through voiceover user when the top alert is closed
|
|
||||||
UIAccessibility.post(notification: .screenChanged, argument: MVMCoreUIUtility.hardcodedString(withKey: "AccTopAlertClosed"))
|
|
||||||
await withCheckedContinuation { continuation in
|
await withCheckedContinuation { continuation in
|
||||||
UIView.animate(withDuration: 0.5) {
|
UIView.animate(withDuration: 0.5) {
|
||||||
self.height.isActive = true
|
self.height.isActive = true
|
||||||
self.superview?.layoutIfNeeded()
|
self.superview?.layoutIfNeeded()
|
||||||
} completion: { finished in
|
} completion: { finished in
|
||||||
UIAccessibility.post(notification: .layoutChanged, argument: nil)
|
|
||||||
self.currentNotificationView?.removeFromSuperview()
|
self.currentNotificationView?.removeFromSuperview()
|
||||||
self.currentNotificationView = nil
|
self.currentNotificationView = nil
|
||||||
continuation.resume()
|
continuation.resume()
|
||||||
|
|||||||
@ -399,7 +399,7 @@ open class NotificationHandler {
|
|||||||
try await showNotification(for: json, delegateObject: delegateObject)
|
try await showNotification(for: json, delegateObject: delegateObject)
|
||||||
} catch {
|
} catch {
|
||||||
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") {
|
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") {
|
||||||
MVMCoreUILoggingHandler.addError(toLog: errorObject)
|
MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,7 @@ open class CoreUIModelMapping: ModelMapping {
|
|||||||
ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self)
|
ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self)
|
||||||
ModelRegistry.register(handler: Video.self, for: VideoModel.self)
|
ModelRegistry.register(handler: Video.self, for: VideoModel.self)
|
||||||
ModelRegistry.register(handler: Tilelet.self, for: TileletModel.self)
|
ModelRegistry.register(handler: Tilelet.self, for: TileletModel.self)
|
||||||
|
ModelRegistry.register(handler: Badge.self, for: BadgeModel.self)
|
||||||
|
|
||||||
// MARK:- Horizontal Combination Molecules
|
// MARK:- Horizontal Combination Molecules
|
||||||
ModelRegistry.register(handler: StringAndMoleculeView.self, for: StringAndMoleculeModel.self)
|
ModelRegistry.register(handler: StringAndMoleculeView.self, for: StringAndMoleculeModel.self)
|
||||||
|
|||||||
@ -9,14 +9,19 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import MVMCore
|
import MVMCore
|
||||||
|
|
||||||
@objcMembers
|
@objcMembers
|
||||||
public class CoreUIObject: NSObject {
|
public class CoreUIObject: NSObject {
|
||||||
private static var singleton = CoreUIObject()
|
private static var singleton = CoreUIObject()
|
||||||
public static func sharedInstance() -> CoreUIObject? { singleton }
|
public static func sharedInstance() -> CoreUIObject? { singleton }
|
||||||
private override init() {}
|
private override init() {}
|
||||||
|
|
||||||
public var alertHandler: AlertHandler?
|
public var alertHandler: AlertHandler?
|
||||||
public var topNotificationHandler: NotificationHandler?
|
public var topNotificationHandler: NotificationHandler? {
|
||||||
|
didSet {
|
||||||
|
accessibilityHandler?.registerForTopNotificationsChanges()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public var accessibilityHandler: AccessibilityHandler?
|
||||||
|
|
||||||
public func defaultInitialSetup() {
|
public func defaultInitialSetup() {
|
||||||
MVMCoreObject.sharedInstance()?.defaultInitialSetup()
|
MVMCoreObject.sharedInstance()?.defaultInitialSetup()
|
||||||
@ -26,5 +31,6 @@ public class CoreUIObject: NSObject {
|
|||||||
MVMCoreObject.sharedInstance()?.viewControllerMapping = MVMCoreUIViewControllerMappingObject()
|
MVMCoreObject.sharedInstance()?.viewControllerMapping = MVMCoreUIViewControllerMappingObject()
|
||||||
MVMCoreObject.sharedInstance()?.loggingDelegate = MVMCoreUILoggingHandler()
|
MVMCoreObject.sharedInstance()?.loggingDelegate = MVMCoreUILoggingHandler()
|
||||||
alertHandler = AlertHandler()
|
alertHandler = AlertHandler()
|
||||||
|
accessibilityHandler = AccessibilityHandler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// MVMCoreUILoggingHandler.h
|
|
||||||
// MVMCoreUI
|
|
||||||
//
|
|
||||||
// Created by Scott Pfeil on 1/10/19.
|
|
||||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
@import MVMCore.MVMCoreLoggingHandler;
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MVMCoreUILoggingHandler : MVMCoreLoggingHandler
|
|
||||||
|
|
||||||
// Page State Logging
|
|
||||||
- (void)defaultLogPageStateForController:(nonnull id <MVMCoreViewControllerProtocol>)controller;
|
|
||||||
|
|
||||||
// Action Logging
|
|
||||||
- (void)defaultLogActionForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData;
|
|
||||||
- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// MVMCoreUILoggingHandler.m
|
|
||||||
// MVMCoreUI
|
|
||||||
//
|
|
||||||
// Created by Scott Pfeil on 1/10/19.
|
|
||||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MVMCoreUILoggingHandler.h"
|
|
||||||
|
|
||||||
@implementation MVMCoreUILoggingHandler
|
|
||||||
|
|
||||||
- (void)defaultLogPageStateForController:(nonnull id <MVMCoreViewControllerProtocol>)controller {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)defaultLogActionForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
16
MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.swift
Normal file
16
MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// MVMCoreUILoggingHandler.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Nandhini Rajendran on 29/09/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
@objc open class MVMCoreUILoggingHandler: MVMCoreLoggingHandler {
|
||||||
|
|
||||||
|
// Page State Logging
|
||||||
|
@objc open func defaultLogPageState(forController controller: MVMCoreViewControllerProtocol) { }
|
||||||
|
|
||||||
|
// Action Logging
|
||||||
|
@objc open func defaultLogAction(forController controller: MVMCoreViewControllerProtocol?, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) { }
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@
|
|||||||
#import <MVMCoreUI/MVMCoreUI-Swift.h>
|
#import <MVMCoreUI/MVMCoreUI-Swift.h>
|
||||||
|
|
||||||
CGFloat const PaddingDefault = 24;
|
CGFloat const PaddingDefault = 24;
|
||||||
CGFloat const PaddingDefaultHorizontalSpacing = 32;
|
CGFloat const PaddingDefaultHorizontalSpacing = 16;
|
||||||
CGFloat const PaddingDefaultVerticalSpacing = 32;
|
CGFloat const PaddingDefaultVerticalSpacing = 32;
|
||||||
CGFloat const PaddingDefaultVerticalSpacing3 = 24;
|
CGFloat const PaddingDefaultVerticalSpacing3 = 24;
|
||||||
CGFloat const PaddingBetweenFields = 24;
|
CGFloat const PaddingBetweenFields = 24;
|
||||||
|
|||||||
@ -159,42 +159,6 @@ open class Styler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Button {
|
|
||||||
|
|
||||||
public enum Style: String, Codable {
|
|
||||||
case primary
|
|
||||||
case secondary
|
|
||||||
}
|
|
||||||
///MVA 3.0 - Button sizes are standard(default size), small, Tiny. Tiny button has been depricated as of Rebranding 3.0.
|
|
||||||
public enum Size: String, Codable {
|
|
||||||
case standard
|
|
||||||
case small
|
|
||||||
case tiny
|
|
||||||
|
|
||||||
func getHeight() -> CGFloat {
|
|
||||||
switch self {
|
|
||||||
case .standard:
|
|
||||||
return 44
|
|
||||||
case .small:
|
|
||||||
return 32
|
|
||||||
case .tiny:
|
|
||||||
return 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func minimumWidth() -> CGFloat {
|
|
||||||
switch self {
|
|
||||||
case .standard:
|
|
||||||
return 76
|
|
||||||
case .small:
|
|
||||||
return 0
|
|
||||||
case .tiny:
|
|
||||||
return 49
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Functions
|
// MARK: - Functions
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
"textfield_picker_item" = " picker item";
|
"textfield_picker_item" = " picker item";
|
||||||
"textfield_regular" = " regular";
|
"textfield_regular" = " regular";
|
||||||
"textfield_disabled_state" = "disabled";
|
"textfield_disabled_state" = "disabled";
|
||||||
|
"textfield_optional" = "Optional";
|
||||||
|
|
||||||
// MARK: MDNTextfield
|
// MARK: MDNTextfield
|
||||||
"textfield_contacts_barbutton" = "My Contacts";
|
"textfield_contacts_barbutton" = "My Contacts";
|
||||||
@ -75,7 +75,9 @@
|
|||||||
|
|
||||||
// MARK: Carousel
|
// MARK: Carousel
|
||||||
"MVMCoreUIPageControl_currentpage_index" = "page %@ of %d";
|
"MVMCoreUIPageControl_currentpage_index" = "page %@ of %d";
|
||||||
"MVMCoreUIPageControlslides_currentpage_index" = "slide %@ of %d";
|
"MVMCoreUIPageControlslides_currentpage_index" = "slide %@ of %d selected";
|
||||||
|
"MVMCoreUIPageControlslides_currentpage_index_accessibilityAnnouncement" = "slide %@ of %d";
|
||||||
|
"MVMCoreUIPageControlslides_totalslides" = "Carousel containing %d slides,";
|
||||||
|
|
||||||
|
|
||||||
// MARK: Styler
|
// MARK: Styler
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
"textfield_picker_item" = " artículo de selector";
|
"textfield_picker_item" = " artículo de selector";
|
||||||
"textfield_regular" = " regular";
|
"textfield_regular" = " regular";
|
||||||
"textfield_disabled_state" = "inactivo";
|
"textfield_disabled_state" = "inactivo";
|
||||||
|
"textfield_optional" = "Opcional";
|
||||||
//MDNTextfield
|
//MDNTextfield
|
||||||
"textfield_contacts_barbutton" = "Mis contactos";
|
"textfield_contacts_barbutton" = "Mis contactos";
|
||||||
"textfield_phone_format_error_message" = "Formato de número de teléfono inválido.";
|
"textfield_phone_format_error_message" = "Formato de número de teléfono inválido.";
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
"textfield_picker_item" = " artículo de selector";
|
"textfield_picker_item" = " artículo de selector";
|
||||||
"textfield_regular" = " regular";
|
"textfield_regular" = " regular";
|
||||||
"textfield_disabled_state" = "inactivo";
|
"textfield_disabled_state" = "inactivo";
|
||||||
|
"textfield_optional" = "Opcional";
|
||||||
//MDNTextfield
|
//MDNTextfield
|
||||||
"textfield_contacts_barbutton" = "Mis contactos";
|
"textfield_contacts_barbutton" = "Mis contactos";
|
||||||
"textfield_phone_format_error_message" = "Formato de número de teléfono inválido.";
|
"textfield_phone_format_error_message" = "Formato de número de teléfono inválido.";
|
||||||
@ -60,7 +61,10 @@
|
|||||||
|
|
||||||
// Carousel
|
// Carousel
|
||||||
"MVMCoreUIPageControl_currentpage_index" = "página %@ de %d";
|
"MVMCoreUIPageControl_currentpage_index" = "página %@ de %d";
|
||||||
"MVMCoreUIPageControlslides_currentpage_index" = "diapositiva %@ of %d";
|
"MVMCoreUIPageControlslides_currentpage_index" = "diapositiva %@ of %d seleccionado";
|
||||||
|
"MVMCoreUIPageControlslides_currentpage_index_accessibilityAnnouncement" = "diapositiva %@ of %d";
|
||||||
|
"MVMCoreUIPageControlslides_totalslides" = "Carrusel contiene %d diapositivas,";
|
||||||
|
|
||||||
//Styler
|
//Styler
|
||||||
"CountDownDay" = " día";
|
"CountDownDay" = " día";
|
||||||
"CountDownHour" = " hora";
|
"CountDownHour" = " hora";
|
||||||
|
|||||||
@ -9,8 +9,11 @@
|
|||||||
#import "MFFonts.h"
|
#import "MFFonts.h"
|
||||||
#import <CoreText/CoreText.h>
|
#import <CoreText/CoreText.h>
|
||||||
#import "MVMCoreUIUtility.h"
|
#import "MVMCoreUIUtility.h"
|
||||||
@import MVMCore.MVMCoreLoggingHandler;
|
#import <MVMCore/MVMCoreLoggingHandlerHelper.h>
|
||||||
|
@import MVMCore.Swift;
|
||||||
@import MVMCore.MVMCoreErrorConstants;
|
@import MVMCore.MVMCoreErrorConstants;
|
||||||
|
@import MVMCore.MVMCoreLoadHandler;
|
||||||
|
@import MVMCore.MVMCoreErrorObject;
|
||||||
|
|
||||||
NSString * const DSBold = @"VerizonNHGeDS-Bold";
|
NSString * const DSBold = @"VerizonNHGeDS-Bold";
|
||||||
NSString * const DSRegular = @"VerizonNHGeDS-Regular";
|
NSString * const DSRegular = @"VerizonNHGeDS-Regular";
|
||||||
@ -108,7 +111,7 @@ NSString * const TXRegular = @"VerizonNHGeTX-Regular";
|
|||||||
+ (void)validFont:(UIFont *)font fontName:(NSString *)fontName {
|
+ (void)validFont:(UIFont *)font fontName:(NSString *)fontName {
|
||||||
if (font == nil) {
|
if (font == nil) {
|
||||||
MVMCoreErrorObject *errorObject = [[MVMCoreErrorObject alloc] initWithTitle:@"font can not load" message:[NSString stringWithFormat:@"missing font name is %@", fontName] code:ErrorCodeFontNotFound domain:ErrorDomainNative location:@"MFStyler"];
|
MVMCoreErrorObject *errorObject = [[MVMCoreErrorObject alloc] initWithTitle:@"font can not load" message:[NSString stringWithFormat:@"missing font name is %@", fontName] code:ErrorCodeFontNotFound domain:ErrorDomainNative location:@"MFStyler"];
|
||||||
[MVMCoreLoggingHandler addErrorToLog:errorObject];
|
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:errorObject];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -39,7 +39,7 @@ public extension MVMCoreUIUtility {
|
|||||||
/// - type: The type you are looking for.
|
/// - type: The type you are looking for.
|
||||||
/// - views: The starting array of subviews.
|
/// - views: The starting array of subviews.
|
||||||
/// - Returns: Will return an array of any view associated with the given type. Will return an empty array of none were found.
|
/// - Returns: Will return an array of any view associated with the given type. Will return an empty array of none were found.
|
||||||
static func findViews<T>(by type: T.Type, views: [UIView]) -> [T] {
|
static func findViews<T>(by type: T.Type, views: [UIView], excludedViews: [UIView] = []) -> [T] {
|
||||||
|
|
||||||
guard !views.isEmpty else { return [] }
|
guard !views.isEmpty else { return [] }
|
||||||
|
|
||||||
@ -47,6 +47,9 @@ public extension MVMCoreUIUtility {
|
|||||||
var matching = [T]()
|
var matching = [T]()
|
||||||
|
|
||||||
for view in views {
|
for view in views {
|
||||||
|
guard !excludedViews.contains(view) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if view is T {
|
if view is T {
|
||||||
matching.append(view as! T)
|
matching.append(view as! T)
|
||||||
}
|
}
|
||||||
@ -54,7 +57,7 @@ public extension MVMCoreUIUtility {
|
|||||||
queue.append(contentsOf: view.subviews)
|
queue.append(contentsOf: view.subviews)
|
||||||
}
|
}
|
||||||
|
|
||||||
return findViews(by: type, views: queue) + matching
|
return findViews(by: type, views: queue, excludedViews: excludedViews) + matching
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
|||||||
35
MVMCoreUI/Utility/RawRepresentableCodable.swift
Normal file
35
MVMCoreUI/Utility/RawRepresentableCodable.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// RawRepresentableCodable.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Scott Pfeil on 9/14/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol RawRepresentableCodable: RawRepresentable, Codable where RawValue: Hashable & Decodable {
|
||||||
|
static var mapping: [RawValue: Self] { get }
|
||||||
|
static var defaultValue: Self? { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RawRepresentableCodableError: Swift.Error {
|
||||||
|
case invalid(value: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RawRepresentableCodable {
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.singleValueContainer()
|
||||||
|
let rawValue = try container.decode(RawValue.self)
|
||||||
|
if let found = Self(rawValue: rawValue) {
|
||||||
|
self = found
|
||||||
|
} else if let found = Self.mapping[rawValue] {
|
||||||
|
self = found
|
||||||
|
} else if let defaultValue = Self.defaultValue {
|
||||||
|
self = defaultValue
|
||||||
|
} else {
|
||||||
|
throw RawRepresentableCodableError.invalid(value: "\(rawValue)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user