merge from develop

This commit is contained in:
Kevin G Christiano 2020-02-07 09:20:39 -05:00
commit d02586f613
35 changed files with 1187 additions and 386 deletions

View File

@ -111,6 +111,10 @@
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; };
946EE1BA237B66D80036751F /* MoleculeModelHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946EE1B9237B66D80036751F /* MoleculeModelHelper.swift */; };
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; };
94AF4A3E23E9D13900676048 /* MFCaretButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 94AF4A3C23E9D13900676048 /* MFCaretButton.h */; settings = {ATTRIBUTES = (Public, ); }; };
94AF4A3F23E9D13900676048 /* MFCaretButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AF4A3D23E9D13900676048 /* MFCaretButton.m */; };
94AF4A4223E9D19E00676048 /* MFCaretView.h in Headers */ = {isa = PBXBuildFile; fileRef = 94AF4A4023E9D19E00676048 /* MFCaretView.h */; settings = {ATTRIBUTES = (Public, ); }; };
94AF4A4323E9D19E00676048 /* MFCaretView.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AF4A4123E9D19E00676048 /* MFCaretView.m */; };
94C2D9842386F3F80006CF46 /* LabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9832386F3F80006CF46 /* LabelAttributeModel.swift */; };
94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A023872BCC0006CF46 /* LabelAttributeUnderlineModel.swift */; };
94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A223872C110006CF46 /* LabelAttributeStrikeThroughModel.swift */; };
@ -120,6 +124,8 @@
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */; };
94C661D923CCF4B400D9FE5B /* LeftRightLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9402C34F23A2CEA3004B974C /* LeftRightLabelModel.swift */; };
94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B33239813C50067DD0F /* UIColor+Extension.swift */; };
94F217B623E0BF6100A47C06 /* PrimaryButtonView.h in Headers */ = {isa = PBXBuildFile; fileRef = 94F217B423E0BF6100A47C06 /* PrimaryButtonView.h */; settings = {ATTRIBUTES = (Public, ); }; };
94F217B723E0BF6100A47C06 /* PrimaryButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = 94F217B523E0BF6100A47C06 /* PrimaryButtonView.m */; };
94FB966223D797DA003D482B /* MFTextButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 94FB966023D797DA003D482B /* MFTextButton.h */; settings = {ATTRIBUTES = (Public, ); }; };
94FB966323D797DA003D482B /* MFTextButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 94FB966123D797DA003D482B /* MFTextButton.m */; };
C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; };
@ -318,12 +324,15 @@
D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99923D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift */; };
D2E2A99C23D8D975000B42E6 /* ImageHeadlineBodyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99B23D8D975000B42E6 /* ImageHeadlineBodyModel.swift */; };
D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */; };
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; };
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; };
D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; };
D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; };
D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; };
DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; };
DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; };
DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; };
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391A224421A0001AB423 /* CaretButton.swift */; };
DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391A224421A0001AB423 /* CaretLink.swift */; };
DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */; };
DBEFFA04225A829700230692 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB891E822253FA8500022516 /* Label.swift */; };
/* End PBXBuildFile section */
@ -425,6 +434,10 @@
9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = "<group>"; };
946EE1B9237B66D80036751F /* MoleculeModelHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeModelHelper.swift; sourceTree = "<group>"; };
948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = "<group>"; };
94AF4A3C23E9D13900676048 /* MFCaretButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFCaretButton.h; sourceTree = "<group>"; };
94AF4A3D23E9D13900676048 /* MFCaretButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFCaretButton.m; sourceTree = "<group>"; };
94AF4A4023E9D19E00676048 /* MFCaretView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFCaretView.h; sourceTree = "<group>"; };
94AF4A4123E9D19E00676048 /* MFCaretView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFCaretView.m; sourceTree = "<group>"; };
94C2D9832386F3F80006CF46 /* LabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeModel.swift; sourceTree = "<group>"; };
94C2D9A023872BCC0006CF46 /* LabelAttributeUnderlineModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeUnderlineModel.swift; sourceTree = "<group>"; };
94C2D9A223872C110006CF46 /* LabelAttributeStrikeThroughModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeStrikeThroughModel.swift; sourceTree = "<group>"; };
@ -432,6 +445,8 @@
94C2D9A623872DA90006CF46 /* LabelAttributeColorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeColorModel.swift; sourceTree = "<group>"; };
94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeImageModel.swift; sourceTree = "<group>"; };
94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeActionModel.swift; sourceTree = "<group>"; };
94F217B423E0BF6100A47C06 /* PrimaryButtonView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrimaryButtonView.h; sourceTree = "<group>"; };
94F217B523E0BF6100A47C06 /* PrimaryButtonView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrimaryButtonView.m; sourceTree = "<group>"; };
94FB966023D797DA003D482B /* MFTextButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFTextButton.h; sourceTree = "<group>"; };
94FB966123D797DA003D482B /* MFTextButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFTextButton.m; sourceTree = "<group>"; };
C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
@ -643,6 +658,9 @@
D2E2A99723D8D63C000B42E6 /* ActionDetailWithImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImageModel.swift; sourceTree = "<group>"; };
D2E2A99923D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButtonModel.swift; sourceTree = "<group>"; };
D2E2A99B23D8D975000B42E6 /* ImageHeadlineBodyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeadlineBodyModel.swift; sourceTree = "<group>"; };
D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = "<group>"; };
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = "<group>"; };
D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = "<group>"; };
D2F4DDE52371A4CB00CD28BB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = "<group>"; };
D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = "<group>"; };
@ -650,7 +668,7 @@
DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = "<group>"; };
DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = "<group>"; };
DBC4391A224421A0001AB423 /* CaretButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretButton.swift; sourceTree = "<group>"; };
DBC4391A224421A0001AB423 /* CaretLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretLink.swift; sourceTree = "<group>"; };
DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelWithInternalButton.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -678,6 +696,7 @@
012A889B23889E8400FE3DA1 /* TemplateModelProtocol.swift */,
D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */,
011B58EF23A2AA980085F53C /* ListItemModelProtocol.swift */,
D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */,
);
path = ModelProtocols;
sourceTree = "<group>";
@ -845,6 +864,13 @@
D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */,
D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */,
D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */,
94F217B423E0BF6100A47C06 /* PrimaryButtonView.h */,
94F217B523E0BF6100A47C06 /* PrimaryButtonView.m */,
D282AACA2243C61700C46919 /* ButtonView.swift */,
94AF4A3C23E9D13900676048 /* MFCaretButton.h */,
94AF4A3D23E9D13900676048 /* MFCaretButton.m */,
94AF4A4023E9D19E00676048 /* MFCaretView.h */,
94AF4A4123E9D19E00676048 /* MFCaretView.m */,
);
path = Views;
sourceTree = "<group>";
@ -1171,13 +1197,13 @@
isa = PBXGroup;
children = (
01F2A03123A4498200D954D8 /* CaretLinkModel.swift */,
DBC4391A224421A0001AB423 /* CaretButton.swift */,
DBC4391A224421A0001AB423 /* CaretLink.swift */,
D28A838A23CCDA6B00DFE4FC /* ButtonModel.swift */,
D282AACA2243C61700C46919 /* ButtonView.swift */,
D2E2A99E23E07F8A000B42E6 /* PillButton.swift */,
D28A838823CCCFCB00DFE4FC /* LinkModel.swift */,
C07065C32395677300FBF997 /* Link.swift */,
D28A838C23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift */,
D28A837623C79FC600DFE4FC /* MFCustomButton+ActionModel.swift */,
C07065C32395677300FBF997 /* Link.swift */,
);
path = Buttons;
sourceTree = "<group>";
@ -1376,6 +1402,7 @@
D2B18B7D236090D500A9AEDC /* BaseClasses */ = {
isa = PBXGroup;
children = (
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */,
C003506023AA94CD00B6AC29 /* Button.swift */,
D2B18B7E2360913400A9AEDC /* Control.swift */,
D2B18B802360945C00A9AEDC /* View.swift */,
@ -1424,6 +1451,7 @@
D29DF27521E79E81003B2FB9 /* MVMCoreUILoggingHandler.h in Headers */,
D29DF28B21E7AC2B003B2FB9 /* ViewConstrainingView.h in Headers */,
D29DF2B321E7B76D003B2FB9 /* MFLoadingSpinner.h in Headers */,
94AF4A3E23E9D13900676048 /* MFCaretButton.h in Headers */,
0A21DB8A235E06EF00C160A2 /* MFDigitTextBox.h in Headers */,
D29DF32521ED0DA2003B2FB9 /* TextButtonView.h in Headers */,
0A21DB8C235E06EF00C160A2 /* MFDigitTextField.h in Headers */,
@ -1442,6 +1470,7 @@
D29DF2CA21E7BFC8003B2FB9 /* MFSizeThreshold.h in Headers */,
D29DF28021E7AA51003B2FB9 /* MVMCoreUIDetailViewProtocol.h in Headers */,
D29DF2BD21E7BEA4003B2FB9 /* MVMCoreUITabBarPageControlViewController.h in Headers */,
94AF4A4223E9D19E00676048 /* MFCaretView.h in Headers */,
D29DF2EE21ECEADF003B2FB9 /* MFFonts.h in Headers */,
D29DF12D21E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h in Headers */,
D29770F321F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.h in Headers */,
@ -1454,6 +1483,7 @@
D29DF2A121E7AF4E003B2FB9 /* MVMCoreUIUtility.h in Headers */,
D29DF17621E69E1F003B2FB9 /* PrimaryButton.h in Headers */,
D29DF2C821E7BFC1003B2FB9 /* MFSizeObject.h in Headers */,
94F217B623E0BF6100A47C06 /* PrimaryButtonView.h in Headers */,
D29DF32021ED0CBA003B2FB9 /* LabelView.h in Headers */,
D29770C921F7C4AE00B2F0D0 /* TopLabelsView.h in Headers */,
D29DF2E121E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h in Headers */,
@ -1590,6 +1620,7 @@
D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */,
01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */,
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */,
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */,
D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */,
D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */,
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */,
@ -1605,6 +1636,7 @@
014AA72D23C5059B006F3E93 /* StackPageTemplateModel.swift in Sources */,
D260106123D0C02A00764D80 /* StackItemModelProtocol.swift in Sources */,
012A88C4238D86E600FE3DA1 /* CarouselItemModelProtocol.swift in Sources */,
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */,
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */,
D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */,
014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */,
@ -1639,6 +1671,7 @@
D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */,
0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */,
0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */,
94F217B723E0BF6100A47C06 /* PrimaryButtonView.m in Sources */,
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */,
0A21DB8B235E06EF00C160A2 /* MFDigitTextBox.m in Sources */,
D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */,
@ -1685,6 +1718,7 @@
944589212385D6E900DE9FD4 /* DashLineModel.swift in Sources */,
D2E2A99623D8CF85000B42E6 /* HeadlineBodyLinkToggleModel.swift in Sources */,
C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */,
94AF4A3F23E9D13900676048 /* MFCaretButton.m in Sources */,
D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */,
D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */,
D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */,
@ -1735,10 +1769,12 @@
D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */,
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */,
0A21DB8D235E06EF00C160A2 /* MFDigitTextField.m in Sources */,
94AF4A4323E9D19E00676048 /* MFCaretView.m in Sources */,
943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */,
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */,
D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */,
D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */,
D29DF11821E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m in Sources */,
94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */,
D28A838523CCCA8900DFE4FC /* ScrollerModel.swift in Sources */,
@ -1749,7 +1785,7 @@
D29770FC21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m in Sources */,
C003506123AA94CD00B6AC29 /* Button.swift in Sources */,
0A14F6A523E4803A00EDF7F7 /* StackView.swift in Sources */,
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */,
DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */,
0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */,
0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */,
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */,

View File

@ -18,14 +18,21 @@ public enum ButtonSize: String, Codable {
case tiny
}
public class ButtonModel: MoleculeModelProtocol {
public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol {
public static var identifier: String = "button"
public var moleculeName: String?
public var backgroundColor: Color?
public var title: String
public var action: ActionModelProtocol
public var enabled: Bool = true
public var style: ButtonStyle?
public var size: ButtonSize? = .standard
public var fillColor: Color?
public var textColor: Color?
public var borderColor: Color?
public var disabledFillColor: Color?
public var disabledTextColor: Color?
public var disabledBorderColor: Color?
public var required: Bool?
public var requiredGroups: [String]?
@ -34,13 +41,26 @@ public class ButtonModel: MoleculeModelProtocol {
self.action = action
}
init(secondaryButtonWith title: String, action: ActionModelProtocol) {
self.title = title
self.action = action
style = .secondary
}
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case title
case action
case enabled
case style
case size
case fillColor
case textColor
case borderColor
case disabledFillColor
case disabledTextColor
case disabledBorderColor
case required
case requiredGroups
}
@ -60,6 +80,15 @@ public class ButtonModel: MoleculeModelProtocol {
if let size = try typeContainer.decodeIfPresent(ButtonSize.self, forKey: .size) {
self.size = size
}
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
self.enabled = enabled
}
fillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor)
textColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor)
borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor)
disabledFillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledFillColor)
disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor)
disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor)
}
public func encode(to encoder: Encoder) throws {
@ -68,8 +97,16 @@ public class ButtonModel: MoleculeModelProtocol {
try container.encode(title, forKey: .title)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeModel(action, forKey: .action)
try container.encode(enabled, forKey: .enabled)
try container.encodeIfPresent(style, forKey: .style)
try container.encodeIfPresent(size, forKey: .size)
try container.encodeIfPresent(fillColor, forKey: .fillColor)
try container.encodeIfPresent(textColor, forKey: .textColor)
try container.encodeIfPresent(borderColor, forKey: .borderColor)
try container.encodeIfPresent(disabledFillColor, forKey: .disabledFillColor)
try container.encodeIfPresent(disabledTextColor, forKey: .disabledTextColor)
try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor)
try container.encodeIfPresent(required, forKey: .required)
try container.encodeIfPresent(requiredGroups, forKey: .requiredGroups)
}
}

View File

@ -1,5 +1,5 @@
//
// CaretButton.swift
// CaretLink.swift
// MVMCoreUI
//
// Created by Kolli, Praneeth on 1/5/18.
@ -8,7 +8,7 @@
//
open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, ModelMoleculeViewProtocol {
open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol {
//------------------------------------------------------
// MARK: - Constants
@ -49,7 +49,9 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
didSet { changeCaretColor() }
}
public func updateView(_ size: CGFloat) { }
public override func updateView(_ size: CGFloat) {
titleLabel?.font = MFStyler.fontB1()
}
//------------------------------------------------------
// MARK: - Methods
@ -61,10 +63,18 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
setTitleColor(disabledColor, for: .disabled)
if let rightCaretView = rightView as? CaretView {
rightCaretView.enabledColor = enabledColor
rightCaretView.disabledColor = disabledColor
rightCaretView.isEnabled = isEnabled
}
}
private func createCaretView() -> CaretView {
let caret = CaretView()
caret.lineWidth = 1.5
return caret
}
private func addCaretImageView() {
rightView?.removeFromSuperview()
@ -75,7 +85,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
let edgeInsets: UIEdgeInsets = contentEdgeInsets
contentEdgeInsets = UIEdgeInsets(top: edgeInsets.top, left: edgeInsets.left, bottom: edgeInsets.bottom, right: 4 + width)
let caretView: UIView = rightView ?? CaretView()
let caretView: UIView = rightView ?? createCaretView()
rightView = caretView
rightView?.translatesAutoresizingMaskIntoConstraints = false
addSubview(caretView)
@ -115,33 +125,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
setTitleColor(disabledColor, for: .disabled)
}
@objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
setWithActionMap(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json else { return }
if let title = dictionary.optionalStringForKey(KeyTitle) {
setTitle(title, for: .normal)
}
if let disableButtonAsAny = dictionary[KeyDisableButton], let isDisabled = disableButtonAsAny as? Bool {
isEnabled = !isDisabled
}
if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String {
backgroundColor = UIColor.mfGet(forHex: backgroundColorHex)
}
if let enabledColorHex = dictionary["enabledColor"] as? String {
enabledColor = UIColor.mfGet(forHex: enabledColorHex)
}
if let disabledColorHex = dictionary["disabledColor"] as? String {
disabledColor = UIColor.mfGet(forHex: disabledColorHex)
}
}
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let caretLinkModel = model as? CaretLinkModel else { return }
if let color = caretLinkModel.backgroundColor {
backgroundColor = color.uiColor
@ -163,7 +147,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
return .leading
}
public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 10
open override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 10.5
}
}

View File

@ -9,14 +9,14 @@
import Foundation
import MVMCore
public class CaretLinkModel: MoleculeModelProtocol {
public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol {
public static var identifier: String = "caretLink"
public var backgroundColor: Color?
public var title: String
public var action: ActionModelProtocol
public var enabledColor: Color = Color(uiColor: .black)
public var disabledColor: Color? = Color(uiColor: .mfSilver())
public var enabled: Bool = true
public var enabled = true
public init(title: String, action: ActionModelProtocol) {
self.title = title

View File

@ -46,7 +46,7 @@ import UIKit
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
}
public static func estimatedHeight(forRow molecule: ModuleMoleculeModel?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
open override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 31.0
}
}

View File

@ -8,7 +8,7 @@
import UIKit
public class LinkModel: MoleculeModelProtocol {
public class LinkModel: ButtonModelProtocol, MoleculeModelProtocol {
public static var identifier: String = "link"
public var backgroundColor: Color?
public var title: String

View File

@ -0,0 +1,179 @@
//
// PillButton.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/28/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
open class PillButton: Button, MVMCoreUIViewConstrainingProtocol, FormValidationEnableDisableProtocol {
// Used to size the button.
var size = MVMCoreUIUtility.getWidth()
var buttonModel: ButtonModel? {
get { return model as? ButtonModel }
}
// Need to re-style on set.
open override var isEnabled: Bool {
didSet {
style()
}
}
private enum ButtonHeight: CGFloat {
case tiny = 20
case standard = 42
}
/// The primary styling for a button. Should be used for main buttons
public func stylePrimary() {
setTitleColor(.white, for: .normal)
setTitleColor(.white, for: .disabled)
layer.borderWidth = 0
if isEnabled {
backgroundColor = .black
} else {
backgroundColor = .mvmCoolGray6
}
}
/// The secondary styling for a button. Should be used for secondary buttons
public func styleSecondary() {
setTitleColor(.black, for: .normal)
setTitleColor(.mvmCoolGray6, for: .disabled)
backgroundColor = .clear
layer.borderWidth = 1
if isEnabled {
layer.borderColor = UIColor.black.cgColor
} else {
layer.borderColor = UIColor.mvmCoolGray6.cgColor
}
}
/// Styles the button based on the model style
private func style() {
switch buttonModel?.style {
case .secondary:
styleSecondary()
default:
stylePrimary()
}
if let titleColor = buttonModel?.textColor {
setTitleColor(titleColor.uiColor, for: .normal)
}
if let disabledTitleColor = buttonModel?.disabledTextColor {
setTitleColor(disabledTitleColor.uiColor, for: .disabled)
}
if isEnabled {
if let fillColor = buttonModel?.fillColor {
backgroundColor = fillColor.uiColor
}
if let borderColor = buttonModel?.borderColor {
layer.borderWidth = 1
layer.borderColor = borderColor.cgColor
}
} else {
if let fillColor = buttonModel?.disabledFillColor {
backgroundColor = fillColor.uiColor
}
if let borderColor = buttonModel?.disabledBorderColor {
layer.borderWidth = 1
layer.borderColor = borderColor.cgColor
}
}
}
private func getInnerPadding() -> CGFloat {
return getHeight() / 2.0
}
private func getHeight() -> CGFloat {
PillButton.getHeight(for: buttonModel?.size, size: size)
}
public static func getHeight(for buttonSize: ButtonSize?, size: CGFloat) -> CGFloat {
switch buttonSize {
case .tiny:
return MFSizeObject(standardSize: ButtonHeight.tiny.rawValue, standardiPadPortraitSize: 34, iPadProLandscapeSize: 38)?.getValueBased(onSize: size) ?? ButtonHeight.tiny.rawValue
default:
return MFSizeObject(standardSize: ButtonHeight.standard.rawValue, standardiPadPortraitSize: 46, iPadProLandscapeSize: 50)?.getValueBased(onSize: size) ?? ButtonHeight.standard.rawValue
}
}
private func getMinimumWidth() -> CGFloat {
switch buttonModel?.size {
case .tiny:
return MFSizeObject(standardSize: 49.0, standardiPadPortraitSize: 90.0, iPadProLandscapeSize: 135.0)?.getValueBased(onSize: size) ?? 49.0
default:
return 151.0
}
}
open override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
let width = size.width + (2 * getInnerPadding())
return CGSize(width: max(width, getMinimumWidth()), height: getHeight())
}
// MARK: - ModelMoleculeViewProtocol
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
// The button will get styled in the enable check in super.
super.setWithModel(model, delegateObject, additionalData)
guard let model = model as? ButtonModel else { return }
setTitle(model.title, for: .normal)
if let required = model.required,
required {
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
}
}
open override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
PillButton.getHeight(for: (molecule as? ButtonModel)?.size, size: MVMCoreUIUtility.getWidth())
}
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) {
super.updateView(size)
self.size = size
invalidateIntrinsicContentSize()
switch buttonModel?.size {
case .tiny:
titleLabel?.font = MFFonts.mfFont75Bd(11 * (intrinsicContentSize.height / ButtonHeight.tiny.rawValue))
default:
titleLabel?.font = MFFonts.mfFont75Bd(13 * (intrinsicContentSize.height / ButtonHeight.standard.rawValue))
}
layer.cornerRadius = getInnerPadding()
}
open override func setupView() {
super.setupView()
titleLabel?.numberOfLines = 1
titleLabel?.lineBreakMode = .byTruncatingTail
titleLabel?.textAlignment = .center
contentHorizontalAlignment = .center
stylePrimary()
}
// MARK: - MVMCoreUIViewConstrainingProtocol
open func horizontalAlignment() -> UIStackView.Alignment {
return .center
}
// MARK: - FormValidationEnableDisableProtocol
public func isValidationRequired() -> Bool {
return buttonModel?.required ?? false
}
public func requiredGroups() -> [String]? {
return buttonModel?.requiredGroups
}
public func enableField(_ enable: Bool) {
isEnabled = isValidationRequired() ? enable : true
}
}

View File

@ -21,7 +21,7 @@ import UIKit
let caret = CaretView()
caret.direction = .down
caret.lineWidth = 1.5
caret.isUserInteractionEnabled = true
caret.isUserInteractionEnabled = false
caret.heightAnchor.constraint(equalToConstant: 9).isActive = true
caret.widthAnchor.constraint(equalToConstant: 16).isActive = true
return caret

View File

@ -25,7 +25,7 @@ import UIKit
//--------------------------------------------------
open private(set) var textField: TextField = {
let textField = TextField(frame: .zero)
let textField = TextField()
textField.isAccessibilityElement = true
textField.setContentCompressionResistancePriority(.required, for: .vertical)
textField.font = MFStyler.fontForTextField()
@ -58,7 +58,7 @@ import UIKit
get { return super.isEnabled }
set (enabled) {
super.isEnabled = enabled
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
@ -99,7 +99,7 @@ import UIKit
//--------------------------------------------------
public var validationBlock: ((_ value: String?) -> Bool)?
//--------------------------------------------------
// MARK: - Delegate Properties
//--------------------------------------------------
@ -169,6 +169,9 @@ import UIKit
textFieldTrailingConstraint = container.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 16)
textFieldTrailingConstraint?.isActive = true
textField.addTarget(self, action: #selector(startEditing), for: .editingDidBegin)
textField.addTarget(self, action: #selector(dismissFieldInput(_:)), for: .editingDidEnd)
let tap = UITapGestureRecognizer(target: self, action: #selector(startEditing))
entryFieldContainer.addGestureRecognizer(tap)
@ -225,7 +228,7 @@ import UIKit
/// Validates the text of the entry field.
@discardableResult
@objc public func validateTextField() -> Bool {
isValid = validationBlock?(text) ?? true
if isValid {
@ -277,7 +280,7 @@ import UIKit
guard let model = model as? TextEntryFieldModel else { return }
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
textColor.enabled = model.enabledTextColor?.uiColor
textColor.disabled = model.disabledTextColor?.uiColor
text = model.text

View File

@ -31,7 +31,7 @@ import UIKit
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
view.backgroundColor = progressObject.progressColor.uiColor
view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: progressObject.progress).isActive = true
view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: progressObject.progress/100.0).isActive = true
view.leadingAnchor.constraint(equalTo: previous?.trailingAnchor ?? leadingAnchor).isActive = true
previous = view
NSLayoutConstraint.constraintPinSubview(view, pinTop: true, pinBottom: true, pinLeft: false, pinRight: false)

View File

@ -91,6 +91,10 @@ public typealias ButtonAction = (Button) -> ()
if let backgroundColor = model?.backgroundColor {
self.backgroundColor = backgroundColor.uiColor
}
guard let model = model as? ButtonModelProtocol else { return }
isEnabled = model.enabled
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
}
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {

View File

@ -0,0 +1,14 @@
//
// ButtonModelProtocol.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/28/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol ButtonModelProtocol: EnableableModelProtocol {
var enabled: Bool { get set }
var action: ActionModelProtocol { get set }
}

View File

@ -6,11 +6,11 @@
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
public typealias ColorHexTuple = (uiColor: UIColor, hex: String)
extension UIColor {
/// Dictionary to access brand approved colors by name.
@ -25,6 +25,7 @@ extension UIColor {
"coolGray1": (.mvmCoolGray1, "#F6F6F6"),
"coolGray3": (.mvmCoolGray3, "#D8DADA"),
"coolGray6": (.mvmCoolGray6, "#747676"),
"coolGray10": (.mvmCoolGray10, "#333333"),
"vzupGold": (.vzupGold, "#B89B56"),
"vzupYellow1": (.vzupYellow1, "#F9D542"),
"vzupYellow2": (.vzupYellow2, "#F4CA53"),
@ -67,6 +68,9 @@ extension UIColor {
/// HEX: #747676
public static let mvmCoolGray6 = UIColor.color8Bits(red: 116, green: 118, blue: 118)
/// HEX: #333333
public static let mvmCoolGray10 = UIColor.grayscale(rgb: 51)
//--------------------------------------------------
// MARK: - VZ UP Brand
//--------------------------------------------------
@ -196,15 +200,4 @@ extension UIColor {
return nil
}
public class func getColorAndHexFromName(_ name: String) -> ColorHexTuple? {
for row in names {
if name == row.key {
return row.value
}
}
return nil
}
}

View File

@ -56,7 +56,7 @@ public final class Color: Codable {
}
init?(name: String) {
guard let colorTuple = UIColor.getColorAndHexFromName(name) else { return nil }
guard let colorTuple = UIColor.names[name] else { return nil }
self.uiColor = colorTuple.uiColor
self.hex = colorTuple.hex
determineRGBA()
@ -73,7 +73,7 @@ public final class Color: Codable {
if colorString.hasPrefix("#") {
hex = colorString.replacingOccurrences(of: "#", with: "")
} else {
guard let hex = UIColor.getColorAndHexFromName(colorString)?.hex else { throw ColorError.badName(reason: "Check the spelling of your color.") }
guard let hex = UIColor.names[colorString]?.hex else { throw ColorError.badName(reason: "Check the spelling of your color.") }
self.hex = hex.replacingOccurrences(of: "#", with: "")
name = colorString
}

View File

@ -7,6 +7,7 @@
//
#import "TopLabelsAndBottomButtonsTableViewController.h"
#import <MVMCoreUI/PrimaryButtonView.h>
#import <MVMCoreUI/MVMCoreUIUtility.h>
#import <MVMCoreUI/StackableViewController.h>
#import <MVMCoreUI/MVMCoreUICommonViewsUtility.h>
@ -121,7 +122,7 @@
// Sets up the buttons/button.
NSDictionary *primaryButtonDictionary = [self primaryButtonMap];
NSDictionary *secondaryButtonDictionary = [self secondaryButtonMap];
TwoButtonView *buttonView = [[TwoButtonView alloc] initWithPrimaryButtonMap:primaryButtonDictionary secondaryButtonMap:secondaryButtonDictionary delegateObject:[self delegateObject] additionalData:nil];
PrimaryButtonView *buttonView = [[PrimaryButtonView alloc] initWithPrimaryButtonMap:primaryButtonDictionary secondaryButtonMap:secondaryButtonDictionary actionDelegate:self additionalData:nil buttonDelegate:self];
self.secondaryButton = buttonView.secondaryButton;
self.primaryButton = buttonView.primaryButton;
@ -439,16 +440,16 @@
- (void)setPrimaryLeftButtonHidden:(BOOL)left rightButtonHidden:(BOOL)right {
if ([self.bottomView isKindOfClass:[TwoButtonView class]]) {
TwoButtonView *buttonView = (TwoButtonView *)self.bottomView;
if ([self.bottomView isKindOfClass:[PrimaryButtonView class]]) {
PrimaryButtonView *buttonView = (PrimaryButtonView *)self.bottomView;
if (right && !left) {
[buttonView hideRightButton];
[buttonView hidePrimaryRightButton];
} else if (!right && left) {
[buttonView hideLeftButton];
[buttonView hidePrimaryLeftButton];
} else if (right && left) {
[buttonView hideBothButtons];
[buttonView hideBothPrimaryButtons];
} else if (!right && !left) {
[buttonView showBothButtons];
[buttonView showBothPrimaryButtons];
}
}
}

View File

@ -7,6 +7,7 @@
//
#import "TopLabelsAndBottomButtonsViewController.h"
#import <MVMCoreUI/PrimaryButtonView.h>
#import <MVMCoreUI/MFSizeObject.h>
#import <MVMCoreUI/MVMCoreUICommonViewsUtility.h>
#import <MVMCoreUI/UIColor+MFConvenience.h>
@ -81,18 +82,18 @@
}
if (!self.customBottemView) {
TwoButtonView *buttonView = (TwoButtonView *)self.bottomView;
PrimaryButtonView *buttonView = (PrimaryButtonView *)self.bottomView;
if (self.secondaryButton || self.primaryButton) {
UIEdgeInsets paddingForBottomButtons = [self paddingForBottomButtons];
[buttonView setLeftPinConstant:paddingForBottomButtons.left];
[buttonView setRightPinConstant:paddingForBottomButtons.right];
buttonView.leftPin.constant = paddingForBottomButtons.left;
buttonView.rightPin.constant = paddingForBottomButtons.right;
buttonView.topPin.constant = paddingForBottomButtons.top;
buttonView.bottomPin.constant = paddingForBottomButtons.bottom;
} else {
buttonView.topPin.constant = 0;
buttonView.bottomPin.constant = 0;
[buttonView setLeftPinConstant:0];
[buttonView setRightPinConstant:0];
buttonView.leftPin.constant = 0;
buttonView.rightPin.constant = 0;
}
}
}
@ -138,7 +139,7 @@
// Sets up the buttons/button.
NSDictionary *primaryButtonDictionary = [self primaryButtonMap];
NSDictionary *secondaryButtonDictionary = [self secondaryButtonMap];
TwoButtonView *buttonView = [[TwoButtonView alloc] initWithPrimaryButtonMap:primaryButtonDictionary secondaryButtonMap:secondaryButtonDictionary delegateObject:[self delegateObject] additionalData:nil];
PrimaryButtonView *buttonView = [[PrimaryButtonView alloc] initWithPrimaryButtonMap:primaryButtonDictionary secondaryButtonMap:secondaryButtonDictionary actionDelegate:self additionalData:nil buttonDelegate:self];
self.secondaryButton = buttonView.secondaryButton;
self.primaryButton = buttonView.primaryButton;
bottomView = buttonView;

View File

@ -0,0 +1,26 @@
//
// MFCaretButton.h
// MobileFirstFramework
//
// Created by Kolli, Praneeth on 1/5/18.
// Copyright © 2018 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCoreUI/MFCustomButton.h>
@interface MFCaretButton : MFCustomButton
@property (nullable, nonatomic, strong) UIView *rightView;
@property (nullable, nonatomic, strong) NSNumber *rightViewHeight;
@property (nullable, nonatomic, strong) NSNumber *rightViewWidth;
-(void)updateCaretSpacing:(CGFloat)spacing;
/* The fill color of the Caret and titleLabel. The default is blackColor */
-(void)setEnableCaretColor:(nullable UIColor *)enableCaretColor;
/* The fill color of the Caret and titleLabel. The default is mfSilver */
-(void)setDisabledCaretColor:(nullable UIColor *)disabledCaretColor;
@end

View File

@ -0,0 +1,113 @@
//
// MFCaretButton.m
// MobileFirstFramework
//
// Created by Kolli, Praneeth on 1/5/18.
// Copyright © 2018 Verizon Wireless. All rights reserved.
//
#import "MFCaretButton.h"
#import "MFCaretView.h"
#import "UIColor+MFConvenience.h"
@interface MFCaretButton ()
@property (nonatomic, strong) UIColor *enableColor;
@property (nonatomic, strong) UIColor *disabledColor;
@property (nonatomic, strong) NSLayoutConstraint *caretSpacingConstraint;
@end
@implementation MFCaretButton
CGFloat const CaretViewHeight = 10.8f;
CGFloat const CaretViewWidth = 6.6f;
- (void)layoutSubviews {
[self addCaretImageView];
[super layoutSubviews];
}
- (void)setEnabled:(BOOL)enabled {
[super setEnabled:enabled];
[self changeCaretColor];
}
- (void)changeCaretColor {
[self setTitleColor:self.enableColor forState:UIControlStateNormal];
[self setTitleColor:self.disabledColor forState:UIControlStateDisabled];
if ([self.rightView isKindOfClass:[MFCaretView class]]) {
MFCaretView *caretView = (MFCaretView *)self.rightView;
if (self.enabled) {
[caretView setLineColor:self.enableColor];
} else {
[caretView setLineColor:self.disabledColor];
}
}
}
- (void)addCaretImageView {
[self.rightView removeFromSuperview];
UIEdgeInsets edgeInsets = self.contentEdgeInsets;
CGFloat rightInset = self.rightViewWidth?self.rightViewWidth.floatValue:CaretViewWidth;
UIEdgeInsets newInsets = UIEdgeInsetsMake(edgeInsets.top, edgeInsets.left, edgeInsets.bottom, 4 + rightInset);
self.contentEdgeInsets = newInsets;
UIView *caretViewIs = self.rightView;
if (!self.rightView) {
caretViewIs = [[MFCaretView alloc]init];
self.rightView = caretViewIs;
}
self.rightView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:self.rightView];
CGFloat width = self.rightViewWidth?self.rightViewWidth.floatValue:CaretViewWidth;
NSLayoutConstraint *caretViewWidthConstraint = [NSLayoutConstraint constraintWithItem:caretViewIs attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:width];
caretViewWidthConstraint.active = YES;
CGFloat height = self.rightViewHeight?self.rightViewHeight.floatValue:CaretViewHeight;
NSLayoutConstraint *caretViewHeightConstraint = [NSLayoutConstraint constraintWithItem:caretViewIs attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:height];
caretViewHeightConstraint.active = YES;
NSLayoutConstraint *caretLabelSpacing = [NSLayoutConstraint constraintWithItem:caretViewIs attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.titleLabel attribute:NSLayoutAttributeRight multiplier:1.0 constant:4.0];
caretLabelSpacing.active = YES;
self.caretSpacingConstraint = caretLabelSpacing;
NSLayoutConstraint *caretCenterY = [NSLayoutConstraint constraintWithItem:caretViewIs attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0];
caretCenterY.active = YES;
NSLayoutConstraint *caretLabelRightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:caretViewIs attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
caretLabelRightPin.active = YES;
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
//set correct color after layout
[self changeCaretColor];
}
- (void)updateCaretSpacing:(CGFloat)spacing {
if (self.caretSpacingConstraint != nil) {
self.caretSpacingConstraint.constant = spacing;
}
}
- (void)setEnableCaretColor:(UIColor *)enableCaretColor {
self.enableColor = enableCaretColor;
[self changeCaretColor];
}
- (void)setDisabledCaretColor:(UIColor *)disabledCaretColor {
self.disabledColor = disabledCaretColor;
[self changeCaretColor];
}
@synthesize enableColor = _enableColor;
- (UIColor *)enableColor {
if (!_enableColor) {
_enableColor = [UIColor blackColor];
}
return _enableColor;
}
@synthesize disabledColor = _disabledColor;
- (UIColor *)disabledColor {
if (!_disabledColor) {
_disabledColor = [UIColor mfSilver];
}
return _disabledColor;
}
@end

View File

@ -0,0 +1,16 @@
//
// MFCaretView.h
// MobileFirstFramework
//
// Created by Kolli, Praneeth on 1/5/18.
// Copyright © 2018 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MFCaretView : UIView
@property (strong, nonatomic, readonly) UIColor *strokeColor;
- (instancetype)initWithLineWidth:(CGFloat)lineWidth;
- (void)setLineColor:(UIColor *)color;
@end

View File

@ -0,0 +1,93 @@
//
// MFCaretView.m
// MobileFirstFramework
//
// Created by Kolli, Praneeth on 1/5/18.
// Copyright © 2018 Verizon Wireless. All rights reserved.
//
#import "MFCaretView.h"
#import "UIColor+MFConvenience.h"
@interface MFCaretView ()
@property (strong, nonatomic, readwrite) UIColor *strokeColor;
@property (nonatomic) NSNumber *lineWidth;
@end
@implementation MFCaretView
- (instancetype)init {
self = [super init];
if (self) {
// Initialization code
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
self.strokeColor = [UIColor blackColor];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
self.strokeColor = [UIColor blackColor];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
// Initialization code
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
self.strokeColor = [UIColor blackColor];
}
return self;
}
- (instancetype)initWithLineWidth:(CGFloat)lineWidth {
if (self = [super init]) {
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
self.lineWidth = @(lineWidth);
self.strokeColor = [UIColor blackColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing Caret
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGFloat lineWidth;
if (self.lineWidth) {
lineWidth = self.lineWidth.floatValue;
} else {
lineWidth = self.frame.size.width/2.6;
}
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(lineWidth/2.0, 0.0)];
[path addLineToPoint:CGPointMake(self.frame.size.width, self.frame.size.height/2.0)];
[path addLineToPoint:CGPointMake(lineWidth/2.0, self.frame.size.height)];
[path addLineToPoint:CGPointMake(0.0, self.frame.size.height-lineWidth/2.0)];
[path addLineToPoint:CGPointMake(self.frame.size.width-lineWidth, self.frame.size.height/2.0)];
[path addLineToPoint:CGPointMake(0.0, lineWidth/2.0)];
[path addLineToPoint:CGPointMake(lineWidth/2.0, 0.0)];
[self.strokeColor setFill];
[path fill];
[path closePath];
[self setBackgroundColor:[UIColor clearColor]];
}
- (void)setLineColor:(UIColor *)color {
self.strokeColor = color;
[self setNeedsDisplay];
}
@end

View File

@ -0,0 +1,63 @@
//
// PrimaryButtonView.h
// myverizon
//
// Created by Scott Pfeil on 12/11/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCoreUI/ViewConstrainingView.h>
#import <MVMCoreUI/PrimaryButton.h>
@interface PrimaryButtonView : ViewConstrainingView
// Set when there is one button or it is the right button when there are two.
@property (nullable, weak, nonatomic) PrimaryButton *primaryButton;
// The left button when there are two buttons.
@property (nullable, weak, nonatomic) PrimaryButton *secondaryButton;
// Inits with a single button.
- (nonnull instancetype)initButtonSmall:(BOOL)small enabled:(BOOL)enabled;
// Inits with two buttons.
- (nonnull instancetype)initWithTwoButtons;
// Legacy: Sets up with whatever is in the passed in button map. (could be 0, 1, or 2 buttons)
- (nonnull instancetype)initButtonSmall:(BOOL)small buttonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate;
- (nonnull instancetype)initWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate;
- (void)setupWithButtonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate;
- (void)setupWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate;
// Sets up with two buttons.
- (void)setupWithTwoButtons;
// For subclassing, just returns the button.
- (nonnull id)createButton;
// Change the alignment of the button
- (void)alignLeft;
- (void)alignCenter;
- (void)alignRight;
- (void)alignFill;//default behavior
// Show/hide left and right primary buttons. This does not impact the vertical space of the view.
- (void)hidePrimaryLeftButton;
- (void)hidePrimaryRightButton;
- (void)showBothPrimaryButtons;
- (void)hideBothPrimaryButtons;
- (void)removeSubviews;
// update with delegateObject
- (nonnull instancetype)initButtonSmall:(BOOL)small buttonMap:(nullable NSDictionary *)buttonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
- (nonnull instancetype)initWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
- (void)setupWithButtonMap:(nullable NSDictionary *)buttonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
- (void)setupWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
@end

View File

@ -0,0 +1,392 @@
//
// PrimaryButtonView.m
// myverizon
//
// Created by Scott Pfeil on 12/11/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import "PrimaryButtonView.h"
#import <MVMCore/NSDictionary+MFConvenience.h>
#import <MVMCore/MVMCoreDispatchUtility.h>
#import <MVMCore/MVMCoreJSONConstants.h>
#import "MVMCoreUICommonViewsUtility.h"
#import "MVMCoreUIConstants.h"
#import "UIColor+MFConvenience.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@import MVMCore.Swift;
@interface PrimaryButtonView ()
@property (weak, nonatomic) UIView *twoButtonView;
@property (weak, nonatomic) NSLayoutConstraint *alignCenterPin;
@property (weak, nonatomic) NSLayoutConstraint *alignCenterLeftPin;
@property (weak, nonatomic) NSLayoutConstraint *alignCenterRightPin;
@property (weak, nonatomic) NSLayoutConstraint *height;
@property (nonatomic, strong) NSArray<NSLayoutConstraint *> *horizontalConstraints;
@end
@implementation PrimaryButtonView
- (void)updateView:(CGFloat)size {
[super updateView:size];
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[self.primaryButton updateView:size];
[self.secondaryButton updateView:size];
}];
}
- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData {
[super setWithJSON:json delegateObject:delegateObject additionalData:additionalData];
NSString *backgroundColorString = [json string:@"backgroundColor"];
if (backgroundColorString) {
self.backgroundColor = [UIColor mfGetColorForHex:backgroundColorString];
}
NSDictionary *primaryButtonMap = [json dict:@"primaryButton"];
NSDictionary *secondaryButtonMap = [json dict:@"secondaryButton"];
[self setupUIWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap];
[self.primaryButton setAsStandardCustom];
[self.secondaryButton setAsSecondaryCustom];
[self.primaryButton setWithJSON:primaryButtonMap delegateObject:delegateObject additionalData:additionalData];
[self.secondaryButton setWithJSON:secondaryButtonMap delegateObject:delegateObject additionalData:additionalData];
}
#pragma mark - Inits
- (instancetype)init {
if (self = [super init]) {
self.backgroundColor = [UIColor clearColor];
self.translatesAutoresizingMaskIntoConstraints = NO;
[self setupWithSingleButton];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
self.backgroundColor = [UIColor clearColor];
self.translatesAutoresizingMaskIntoConstraints = NO;
[self setupWithSingleButton];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
self.translatesAutoresizingMaskIntoConstraints = NO;
[self setupWithSingleButton];
}
return self;
}
- (nonnull instancetype)initButtonSmall:(BOOL)small enabled:(BOOL)enabled {
if (self = [self init]) {
[self.primaryButton setAsSmallButton:small];
[self.primaryButton setEnabled:enabled];
}
return self;
}
- (nonnull instancetype)initWithTwoButtons {
if (self = [self init]) {
[self setupWithTwoButtons];
}
return self;
}
#pragma mark - Legacy Setup
- (nonnull instancetype)initButtonSmall:(BOOL)small buttonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate {
if (self = [self init]) {
[self setupWithButtonMap:buttonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
[self.primaryButton setAsSmallButton:small];
[self.secondaryButton setAsSmallButton:small];
}
return self;
}
- (nonnull instancetype)initWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate {
if (self = [self init]) {
[self setupWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
}
return self;
}
- (void)setupWithButtonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate {
NSDictionary *secondaryButtonMap = [buttonMap dict:KeySecondaryButton];
NSDictionary *primaryButtonMap = [buttonMap dict:KeyPrimaryButton];
[self setupWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
}
- (void)setupWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap actionDelegate:(nullable NSObject <MVMCoreActionDelegateProtocol>*)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id <ButtonDelegateProtocol>)buttonDelegate {
[self setupUIWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap];
if (primaryButtonMap && secondaryButtonMap) {
[self.primaryButton setWithActionMap:primaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
[self.secondaryButton setWithActionMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
} else if (primaryButtonMap) {
[self.primaryButton setWithActionMap:primaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
self.primaryButton.bordered = NO;
} else {
[self.primaryButton setWithActionMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
self.primaryButton.bordered = YES;
}
}
#pragma mark - Setup
- (void)setupUIWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap {
if (primaryButtonMap && secondaryButtonMap) {
self.height.active = NO;
// Setup with two buttons
if (!self.primaryButton || !self.secondaryButton) {
[self removeSubviews];
self.twoButtonView = nil;
[self setupWithTwoButtons];
}
} else if (primaryButtonMap || secondaryButtonMap) {
self.height.active = NO;
// Setup with one button.
if (!self.primaryButton || self.secondaryButton) {
[self removeSubviews];
self.primaryButton = nil;
self.secondaryButton = nil;
[self setupWithSingleButton];
}
} else {
[self removeSubviews];
if (!self.height) {
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0];
height.active = YES;
self.height = height;
}
}
}
- (id)createButton {
return [PrimaryButton primaryButton:NO];
}
- (void)setupWithTwoButtons {
if (self.primaryButton) {
[self.primaryButton removeFromSuperview];
self.primaryButton = nil;
}
if (!self.twoButtonView) {
self.backgroundColor = [UIColor clearColor];
self.translatesAutoresizingMaskIntoConstraints = NO;
UIView *buttonView = [MVMCoreUICommonViewsUtility commonView];
PrimaryButton *leftButton = [PrimaryButton primaryButton];
leftButton.bordered = YES;
leftButton.translatesAutoresizingMaskIntoConstraints = NO;
[buttonView addSubview:leftButton];
self.secondaryButton = leftButton;
PrimaryButton *rightButton = [PrimaryButton primaryButton];
rightButton.translatesAutoresizingMaskIntoConstraints = NO;
[buttonView addSubview:rightButton];
self.primaryButton = rightButton;
[rightButton.widthAnchor constraintEqualToAnchor:leftButton.widthAnchor multiplier:1].active = YES;
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[leftButton]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(leftButton)]];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[rightButton]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(rightButton)]];
self.horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[leftButton]-10-[rightButton]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(leftButton,rightButton)];
[NSLayoutConstraint activateConstraints:self.horizontalConstraints];
[self addSubview:buttonView];
[self setupAlignmentsWithView:buttonView];
self.twoButtonView = buttonView;
[self alignCenter];
}
}
- (void)setupWithSingleButton {
if (!self.primaryButton) {
self.backgroundColor = [UIColor clearColor];
self.translatesAutoresizingMaskIntoConstraints = NO;
PrimaryButton *button = [self createButton];
button.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:button];
self.primaryButton = button;
[self setupAlignmentsWithView:button];
[self alignCenter];
}
}
- (void)setupAlignmentsWithView:(UIView *)view {
// Align left and right constants.
NSLayoutConstraint *leftPin = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
self.leftPin = leftPin;
leftPin.active = YES;
NSLayoutConstraint *topPin = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
self.topPin = topPin;
topPin.active = YES;
NSLayoutConstraint *bottomPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
self.bottomPin = bottomPin;
bottomPin.active = YES;
NSLayoutConstraint *rightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
self.rightPin = rightPin;
rightPin.active = YES;
// Center alignments
NSLayoutConstraint *alignCenter = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
self.alignCenterPin = alignCenter;
alignCenter.active = YES;
NSLayoutConstraint *centerLeftPin = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
self.alignCenterLeftPin = centerLeftPin;
centerLeftPin.active = YES;
NSLayoutConstraint *centerRightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
self.alignCenterRightPin = centerRightPin;
centerRightPin.active = YES;
}
#pragma mark - Configuring
- (void)alignLeft {
self.alignCenterPin.active = NO;
self.alignCenterLeftPin.active = NO;
self.alignCenterRightPin.active = YES;
self.leftPin.active = YES;
self.rightPin.active = NO;
}
- (void)alignCenter {
self.alignCenterPin.active = YES;
self.alignCenterLeftPin.active = YES;
self.alignCenterRightPin.active = YES;
self.leftPin.active = NO;
self.rightPin.active = NO;
}
- (void)alignFill {
self.alignCenterPin.active = NO;
self.alignCenterLeftPin.active = NO;
self.alignCenterRightPin.active = NO;
self.leftPin.active = YES;
self.rightPin.active = YES;
}
- (void)alignRight {
self.alignCenterPin.active = NO;
self.alignCenterLeftPin.active = YES;
self.alignCenterRightPin.active = NO;
self.leftPin.active = NO;
self.rightPin.active = YES;
}
- (void)setLeftPinConstant:(CGFloat)constant {
[super setLeftPinConstant:constant];
self.alignCenterLeftPin.constant = constant;
}
- (void)setRightPinConstant:(CGFloat)constant {
[super setRightPinConstant:constant];
self.alignCenterRightPin.constant = constant;
}
- (void)resetConstraints {
[super resetConstraints];
self.primaryButton.enabled = NO;
}
- (void)hidePrimaryLeftButton {
if (!self.secondaryButton.hidden) {
self.secondaryButton.hidden = YES;
PrimaryButton *rightButton = self.primaryButton;
[NSLayoutConstraint deactivateConstraints:self.horizontalConstraints];
self.horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[rightButton]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(rightButton)];
[NSLayoutConstraint activateConstraints:self.horizontalConstraints];
}
}
- (void)hidePrimaryRightButton {
if (!self.primaryButton.hidden) {
self.primaryButton.hidden = YES;
PrimaryButton *leftButton = self.secondaryButton;
[NSLayoutConstraint deactivateConstraints:self.horizontalConstraints];
self.horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[leftButton]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(leftButton)];
[NSLayoutConstraint activateConstraints:self.horizontalConstraints];
}
}
- (void)showBothPrimaryButtons {
self.primaryButton.hidden = NO;
self.secondaryButton.hidden = NO;
PrimaryButton *rightButton = self.primaryButton;
PrimaryButton *leftButton = self.secondaryButton;
[NSLayoutConstraint deactivateConstraints:self.horizontalConstraints];
self.horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[leftButton]-10-[rightButton]-0-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(leftButton,rightButton)];
[NSLayoutConstraint activateConstraints:self.horizontalConstraints];
}
- (void)hideBothPrimaryButtons {
self.primaryButton.hidden = YES;
self.secondaryButton.hidden = YES;
}
- (void)removeSubviews {
for (UIView *view in self.subviews) {
[view removeFromSuperview];
}
}
// update with delegateObject
- (nonnull instancetype)initButtonSmall:(BOOL)small buttonMap:(nullable NSDictionary *)buttonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData {
NSObject<ButtonDelegateProtocol> *buttonDelegate = nil;
NSObject<MVMCoreActionDelegateProtocol> *actionDelegate = delegateObject.actionDelegate;
if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) {
buttonDelegate = ((MVMCoreUIDelegateObject *)delegateObject).buttonDelegate;
}
return [self initButtonSmall:small buttonMap:buttonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
}
- (nonnull instancetype)initWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData {
NSObject<ButtonDelegateProtocol> *buttonDelegate = nil;
NSObject<MVMCoreActionDelegateProtocol> *actionDelegate = delegateObject.actionDelegate;
if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) {
buttonDelegate = ((MVMCoreUIDelegateObject *)delegateObject).buttonDelegate;
}
return [self initWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
}
- (void)setupWithButtonMap:(nullable NSDictionary *)buttonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData {
NSObject<ButtonDelegateProtocol> *buttonDelegate = nil;
NSObject<MVMCoreActionDelegateProtocol> *actionDelegate = delegateObject.actionDelegate;
if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) {
buttonDelegate = ((MVMCoreUIDelegateObject *)delegateObject).buttonDelegate;
}
[self setupWithButtonMap:buttonMap actionDelegate:actionDelegate additionalData:nil buttonDelegate:buttonDelegate];
}
- (void)setupWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap delegateObject:(nullable DelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData {
NSObject<ButtonDelegateProtocol> *buttonDelegate = nil;
NSObject<MVMCoreActionDelegateProtocol> *actionDelegate = delegateObject.actionDelegate;
if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) {
buttonDelegate = ((MVMCoreUIDelegateObject *)delegateObject).buttonDelegate;
}
[self setupWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate];
}
@end

View File

@ -81,6 +81,7 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[];
#import <MVMCoreUI/MVMCoreUICheckBox.h>
#import <MVMCoreUI/MVMCoreUISwitch.h>
#import <MVMCoreUI/MFRadioButton.h>
#import <MVMCoreUI/MFCaretView.h>
#pragma mark Buttons
#import <MVMCoreUI/MFButtonProtocol.h>
@ -88,6 +89,8 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[];
#import <MVMCoreUI/MFCustomButton.h>
#import <MVMCoreUI/PrimaryButton.h>
#import <MVMCoreUI/MFTextButton.h>
#import <MVMCoreUI/PrimaryButtonView.h>
#import <MVMCoreUI/MFCaretButton.h>
#pragma mark TextFields
#import <MVMCoreUI/MFTextField.h>

View File

@ -0,0 +1,13 @@
//
// EnableableModelProtocol.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 1/28/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol EnableableModelProtocol {
var enabled: Bool { get set }
}

View File

@ -8,12 +8,12 @@
import UIKit
@objcMembers open class TwoButtonView: ViewConstrainingView {
open var primaryButton: PrimaryButton? = PrimaryButton.button()
open var secondaryButton: PrimaryButton? = PrimaryButton.button()
open var viewForButtons: UIView?
public var heightConstraint: NSLayoutConstraint?
@objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol {
open var primaryButton: PillButton = PillButton()
open var secondaryButton: PillButton = PillButton()
private var stack = UIStackView()
private var equalWidthConstraint: NSLayoutConstraint?
public init() {
super.init(frame: .zero)
}
@ -26,289 +26,100 @@ import UIKit
super.init(frame: frame)
}
public func setDefaultCustom() {
primaryButton?.setAsStandardCustom()
secondaryButton?.setAsSecondaryCustom()
public func setDefault() {
primaryButton.stylePrimary()
secondaryButton.styleSecondary()
}
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) {
super.updateView(size)
MVMCoreDispatchUtility.performBlock(onMainThread: {
self.primaryButton?.updateView(size)
self.secondaryButton?.updateView(size)
})
self.primaryButton.updateView(size)
self.secondaryButton.updateView(size)
}
open override func setupView() {
super.setupView()
setupWithTwoButtons()
secondaryButton?.bordered = true
stack.translatesAutoresizingMaskIntoConstraints = false
addSubview(stack)
stack.addArrangedSubview(secondaryButton)
stack.addArrangedSubview(primaryButton)
NSLayoutConstraint.constraintPinSubview(toSuperview: stack)
stack.axis = .horizontal
stack.spacing = 10
equalWidthConstraint = secondaryButton.widthAnchor.constraint(equalTo: primaryButton.widthAnchor, multiplier: 1)
equalWidthConstraint?.isActive = true
}
// MARK: - Stack Manipulation
public func showPrimaryButton() {
if !stack.arrangedSubviews.contains(primaryButton) {
stack.addArrangedSubview(primaryButton)
primaryButton.isHidden = false
}
if secondaryButton.superview != nil {
equalWidthConstraint?.isActive = true
}
}
public func showSecondaryButton() {
if !stack.arrangedSubviews.contains(secondaryButton) {
stack.insertArrangedSubview(secondaryButton, at: 0)
secondaryButton.isHidden = false
}
if primaryButton.superview != nil {
equalWidthConstraint?.isActive = true
}
}
public func hidePrimaryButton() {
if primaryButton.superview != nil {
stack.removeArrangedSubview(primaryButton)
primaryButton.isHidden = true
}
equalWidthConstraint?.isActive = false
}
public func hideSecondaryButton() {
if secondaryButton.superview != nil {
stack.removeArrangedSubview(secondaryButton)
secondaryButton.isHidden = true
}
equalWidthConstraint?.isActive = false
}
// MARK: - MVMCoreUIMoleculeViewProtocol
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
let primaryButtonMap = json?.optionalDictionaryForKey("primaryButton")
let secondaryButtonMap = json?.optionalDictionaryForKey("secondaryButton")
set(primaryButtonJSON: primaryButtonMap, secondaryButtonJSON: secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
}
open override func reset() {
super.reset()
primaryButton?.setAsStandardCustom()
secondaryButton?.setAsSecondaryCustom()
setDefault()
}
// MARK: - Constraining
func createPrimaryButton() {
if primaryButton == nil {
primaryButton = PrimaryButton.button()
}
}
func createSecondaryButton() {
if secondaryButton == nil {
secondaryButton = PrimaryButton.button()
secondaryButton?.bordered = true
}
}
func removeButtons() {
viewForButtons?.removeFromSuperview()
primaryButton?.removeFromSuperview()
secondaryButton?.removeFromSuperview()
viewForButtons = nil
secondaryButton = nil
primaryButton = nil
}
open func setupConstraintsForViewWithButtons() {
guard let viewForButtons = viewForButtons,
let primaryButton = primaryButton,
let secondaryButton = secondaryButton
else { return }
viewForButtons.addSubview(primaryButton)
viewForButtons.addSubview(secondaryButton)
secondaryButton.widthAnchor.constraint(equalTo: primaryButton.widthAnchor, multiplier: 1).isActive = true
NSLayoutConstraint.constraintPinSubview(secondaryButton, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false)
NSLayoutConstraint.constraintPinSubview(primaryButton, pinTop: true, pinBottom: true, pinLeft: false, pinRight: true)
let constraint = primaryButton.leadingAnchor.constraint(equalTo: secondaryButton.trailingAnchor, constant: 10)
constraint.priority = UILayoutPriority(900)
constraint.isActive = true
}
func setupWithTwoButtons() {
guard viewForButtons == nil else {
return
}
let viewForButtons = MVMCoreUICommonViewsUtility.commonView()
addSubview(viewForButtons)
self.viewForButtons = viewForButtons
pinView(toSuperView: viewForButtons)
alignCenterHorizontal()
createPrimaryButton()
createSecondaryButton()
setupConstraintsForViewWithButtons()
}
open func setupWithPrimaryButton() {
guard primaryButton == nil && secondaryButton == nil else {
return
}
createPrimaryButton()
if let primaryButton = primaryButton {
addSubview(primaryButton)
pinView(toSuperView: primaryButton)
alignCenterHorizontal()
}
}
open func setupWithSecondaryButton() {
guard secondaryButton == nil && primaryButton == nil else {
return
}
createSecondaryButton()
if let secondaryButton = secondaryButton {
addSubview(secondaryButton)
pinView(toSuperView: secondaryButton)
alignCenterHorizontal()
}
}
/// Legacy
func setupUI(withPrimaryButtonMap primaryButtonMap: [AnyHashable: Any]?, secondaryButtonMap: [AnyHashable: Any]?) {
setupUI(primaryButtonShowing: primaryButtonMap != nil, secondaryButtonShowing: secondaryButtonMap != nil)
}
func setupUI(primaryButtonShowing: Bool, secondaryButtonShowing: Bool) {
if primaryButtonShowing, secondaryButtonShowing {
heightConstraint?.isActive = false
if primaryButton == nil || secondaryButton == nil {
removeButtons()
setupWithTwoButtons()
}
} else if primaryButtonShowing {
heightConstraint?.isActive = false
if primaryButton == nil || secondaryButton != nil {
removeButtons()
setupWithPrimaryButton()
}
} else if secondaryButtonShowing {
heightConstraint?.isActive = false
if secondaryButton == nil || primaryButton != nil {
removeButtons()
setupWithSecondaryButton()
}
} else {
removeButtons()
if heightConstraint == nil {
heightConstraint = heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
}
}
open func set(primaryButtonJSON: [AnyHashable: Any]?, secondaryButtonJSON: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
setupUI(withPrimaryButtonMap: primaryButtonJSON, secondaryButtonMap: secondaryButtonJSON)
setDefaultCustom()
primaryButton?.setWithJSON(primaryButtonJSON, delegateObject: delegateObject, additionalData: additionalData)
secondaryButton?.setWithJSON(secondaryButtonJSON, delegateObject: delegateObject, additionalData: additionalData)
}
// MARK: - Legacy
public convenience init(primaryButtonMap: [AnyHashable: Any]?, secondaryButtonMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) {
self.init()
setup(primaryButtonMap: primaryButtonMap, secondaryButtonMap: secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
}
public convenience init(buttonSmall small: Bool, buttonMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: AnyHashable]?) {
self.init()
setup(withButtonMap: buttonMap, delegateObject: delegateObject, additionalData: additionalData)
primaryButton?.setAsSmall(small)
secondaryButton?.setAsSmall(small)
}
public convenience init(buttonSmall small: Bool, enabled: Bool) {
self.init()
removeButtons()
setupWithPrimaryButton()
primaryButton?.setAsSmall(small)
primaryButton?.isEnabled = enabled
}
open func setup(primaryButtonMap: [AnyHashable: Any]?, secondaryButtonMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) {
setupUI(withPrimaryButtonMap: primaryButtonMap, secondaryButtonMap: secondaryButtonMap)
if primaryButtonMap != nil, secondaryButtonMap != nil {
primaryButton?.setWithActionMap(primaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
secondaryButton?.setWithActionMap(secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
} else if primaryButtonMap != nil {
primaryButton?.setWithActionMap(primaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
primaryButton?.bordered = false
} else if secondaryButtonMap != nil {
secondaryButton?.setWithActionMap(secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
secondaryButton?.bordered = true
}
}
open func setup(withButtonMap buttonMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) {
let secondaryButtonMap = buttonMap?.optionalDictionaryForKey(KeySecondaryButton)
let primaryButtonMap = buttonMap?.optionalDictionaryForKey(KeyPrimaryButton)
setup(primaryButtonMap: primaryButtonMap, secondaryButtonMap: secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
}
public func hideLeftButton() {
guard let secondaryButton = secondaryButton, !secondaryButton.isHidden else {
return
}
secondaryButton.isHidden = true
if let primaryButton = primaryButton {
primaryButton.removeFromSuperview()
viewForButtons?.addSubview(primaryButton)
NSLayoutConstraint.constraintPinSubview(toSuperview: primaryButton)
}
}
public func hideRightButton() {
guard let primaryButton = primaryButton, !primaryButton.isHidden else {
return
}
primaryButton.isHidden = true
if let secondaryButton = secondaryButton {
secondaryButton.removeFromSuperview()
viewForButtons?.addSubview(secondaryButton)
NSLayoutConstraint.constraintPinSubview(toSuperview: secondaryButton)
}
}
public func showBothButtons() {
primaryButton?.isHidden = false
secondaryButton?.isHidden = false
if let primaryButton = primaryButton, let secondaryButton = secondaryButton {
primaryButton.removeFromSuperview()
secondaryButton.removeFromSuperview()
setupConstraintsForViewWithButtons()
}
}
public func hideBothButtons() {
primaryButton?.isHidden = true
secondaryButton?.isHidden = true
}
override open func horizontalAlignment() -> UIStackView.Alignment {
// MARK: - MVMCoreUIViewConstrainingProtocol
open func horizontalAlignment() -> UIStackView.Alignment {
return .center
}
}
// MARK: - Deprecate
extension TwoButtonView {
@available(*, deprecated)
open func setup(primaryButtonMap: [AnyHashable: Any]?, secondaryButtonMap: [AnyHashable: Any]?, actionDelegate: NSObjectProtocol?, additionalData: [AnyHashable: Any]?, buttonDelegate: Any?) {
setupUI(withPrimaryButtonMap: primaryButtonMap, secondaryButtonMap: secondaryButtonMap)
if primaryButtonMap != nil, secondaryButtonMap != nil {
primaryButton?.setWithActionMap(primaryButtonMap, actionDelegate: actionDelegate as? MVMCoreActionDelegateProtocol & NSObjectProtocol, additionalData: additionalData, buttonDelegate: buttonDelegate as? ButtonDelegateProtocol)
secondaryButton?.setWithActionMap(secondaryButtonMap, actionDelegate: actionDelegate as? MVMCoreActionDelegateProtocol & NSObjectProtocol, additionalData: additionalData, buttonDelegate: buttonDelegate as? ButtonDelegateProtocol)
} else if primaryButtonMap != nil {
primaryButton?.setWithActionMap(primaryButtonMap, actionDelegate: actionDelegate as? MVMCoreActionDelegateProtocol & NSObjectProtocol, additionalData: additionalData, buttonDelegate: buttonDelegate as? ButtonDelegateProtocol)
primaryButton?.bordered = false
} else if secondaryButtonMap != nil {
secondaryButton?.setWithActionMap(secondaryButtonMap, actionDelegate: actionDelegate as? MVMCoreActionDelegateProtocol & NSObjectProtocol, additionalData: additionalData, buttonDelegate: buttonDelegate as? ButtonDelegateProtocol)
secondaryButton?.bordered = true
// MARK: - ModelMoleculeViewProtocol
public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
guard let model = molecule as? TwoButtonViewModel else { return 0 }
return PillButton.estimatedHeight(forRow: model.primaryButton ?? model.secondaryButton, delegateObject: delegateObject)
}
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let model = model as? TwoButtonViewModel else { return }
if let secondaryModel = model.secondaryButton {
showSecondaryButton()
secondaryButton.setWithModel(secondaryModel, delegateObject, additionalData)
} else {
hideSecondaryButton()
}
if let primaryModel = model.primaryButton {
showPrimaryButton()
primaryButton.setWithModel(primaryModel, delegateObject, additionalData)
} else {
hidePrimaryButton()
}
}
@available(*, deprecated)
open func setup(withButtonMap buttonMap: [AnyHashable: Any]?, actionDelegate: NSObjectProtocol?, additionalData: [AnyHashable: Any]?, buttonDelegate: Any?) {
let secondaryButtonMap = buttonMap?.optionalDictionaryForKey(KeySecondaryButton)
let primaryButtonMap = buttonMap?.optionalDictionaryForKey(KeyPrimaryButton)
setup(primaryButtonMap: primaryButtonMap, secondaryButtonMap: secondaryButtonMap, actionDelegate: actionDelegate, additionalData: additionalData, buttonDelegate: buttonDelegate)
}
@available(*, deprecated)
public convenience init(buttonSmall small: Bool, buttonMap: [AnyHashable: Any]?, actionDelegate: NSObjectProtocol?, additionalData: [AnyHashable: AnyHashable]?, buttonDelegate: Any?) {
self.init()
setup(withButtonMap: buttonMap, actionDelegate: actionDelegate, additionalData: additionalData, buttonDelegate: buttonDelegate)
primaryButton?.setAsSmall(small)
secondaryButton?.setAsSmall(small)
}
@available(*, deprecated)
public convenience init(primaryButtonMap: [AnyHashable: Any]?, secondaryButtonMap: [AnyHashable: Any]?, actionDelegate: NSObjectProtocol?, additionalData: [AnyHashable: Any]?, buttonDelegate: Any?) {
self.init()
setup(primaryButtonMap: primaryButtonMap, secondaryButtonMap: secondaryButtonMap, actionDelegate: actionDelegate, additionalData: additionalData, buttonDelegate: buttonDelegate)
}
}
extension TwoButtonView: ModelMoleculeViewProtocol {
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let model = model as? TwoButtonViewModel else { return }
setupUI(primaryButtonShowing: model.primaryButton != nil, secondaryButtonShowing: model.secondaryButton != nil)
setDefaultCustom()
primaryButton?.setWithModel(model.primaryButton, delegateObject, additionalData)
secondaryButton?.setWithModel(model.secondaryButton, delegateObject, additionalData)
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
}
}

View File

@ -8,9 +8,35 @@
import UIKit
public struct TwoButtonViewModel: MoleculeModelProtocol {
public class TwoButtonViewModel: MoleculeModelProtocol {
public static var identifier: String = "twoButtonView"
public var backgroundColor: Color?
public var primaryButton: ButtonModel?
public var secondaryButton: ButtonModel?
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case primaryButton
case secondaryButton
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
primaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .primaryButton)
secondaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .secondaryButton)
// Default value
if secondaryButton?.style == nil {
secondaryButton?.style = .secondary
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(primaryButton, forKey: .primaryButton)
try container.encodeIfPresent(secondaryButton, forKey: .secondaryButton)
}
}

View File

@ -60,7 +60,7 @@ import UIKit
bottomSeparatorView?.setStyle(.none)
}
public override static func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 80
}
}

View File

@ -171,10 +171,8 @@ import UIKit
guard let model = model as? CornerLabelsModel else { return }
if middleView != nil {
(middleView as? ModelMoleculeViewProtocol)?.setWithModel(model, delegateObject, additionalData)
} else {
if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model.molecule, delegateObject) {
addMiddleView(molecule)
}
} else if let moleculeModel = model.molecule, let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moleculeModel, delegateObject) {
addMiddleView(molecule)
}
topLeftLabel.setWithModel(model.topLeftLabel, delegateObject, additionalData)

View File

@ -15,9 +15,9 @@ public class CornerLabelsModel: MoleculeModelProtocol {
public var topRightLabel: LabelModel?
public var bottomLeftLabel: LabelModel?
public var bottomRightLabel: LabelModel?
public var molecule: MoleculeModelProtocol
public var molecule: MoleculeModelProtocol?
init(with molecule: MoleculeModelProtocol) {
init(with molecule: MoleculeModelProtocol?) {
self.molecule = molecule
}
@ -33,7 +33,7 @@ public class CornerLabelsModel: MoleculeModelProtocol {
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
molecule = try typeContainer.decodeMolecule(codingKey: .molecule)
molecule = try typeContainer.decodeMoleculeIfPresent(codingKey: .molecule)
topLeftLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .topLeftLabel)
topRightLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .topRightLabel)
bottomLeftLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .bottomLeftLabel)

View File

@ -9,9 +9,8 @@
import Foundation
@objcMembers public class HeadLineBodyCaretLinkImage: Container {
let headlineBody = HeadlineBody(frame: .zero)
let caretButton = CaretButton(frame: .zero)
let caretButton = CaretLink(frame: .zero)
let backgroundImageView = MFLoadImageView(pinnedEdges: .all)
var spaceBetweenConstant: CGFloat = 104.0
let maxWidth: CGFloat = 350.0
static let heightConstant: CGFloat = 320.0
var heightConstraint: NSLayoutConstraint?
@ -44,17 +43,17 @@ import Foundation
headlineBody.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true
headlineBody.topAnchor.constraint(equalTo: container.topAnchor, constant: 0).isActive = true
let headLineBodyWidth = headlineBody.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.85)
headLineBodyWidth.priority = .defaultHigh
headlineBody.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.67).isActive = true
let headLineBodyWidth = headlineBody.widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth)
headLineBodyWidth.priority = UILayoutPriority(250)
headLineBodyWidth.isActive = true
headlineBody.widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true
//Caret view
caretButton.translatesAutoresizingMaskIntoConstraints = false
caretButton.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true
container.bottomAnchor.constraint(equalTo: caretButton.bottomAnchor, constant: 0).isActive = true
caretButton.topAnchor.constraint(greaterThanOrEqualTo: headlineBody.bottomAnchor, constant: spaceBetweenConstant).isActive = true
caretButton.topAnchor.constraint(greaterThanOrEqualTo: headlineBody.bottomAnchor, constant: PaddingTwo).isActive = true
//Background image view
backgroundImageView.translatesAutoresizingMaskIntoConstraints = false
@ -67,13 +66,6 @@ import Foundation
}
// MARK: - MVMCoreUIMoleculeViewProtocol
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
backgroundImageView.setWithJSON(json?.optionalDictionaryForKey("image"), delegateObject: delegateObject, additionalData: additionalData)
headlineBody.setWithJSON(json?.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData)
caretButton.setWithJSON(json?.optionalDictionaryForKey("caretLink"), delegateObject: delegateObject, additionalData: additionalData)
}
open override func reset() {
super.reset()
headlineBody.reset()

View File

@ -33,8 +33,9 @@ open class Stack<T>: Container where T: StackModelProtocol {
})
// Adds the views
let totalSpace = getTotalSpace()
for (index, view) in stackItems.enumerated() {
addView(view, stackModel.molecules[index], percentModifier: getPercentModifier(), lastItem: lastItemIndex == index)
addView(view, stackModel.molecules[index], totalSpacing: totalSpace, lastItem: lastItemIndex == index)
}
}
@ -173,14 +174,12 @@ open class Stack<T>: Container where T: StackModelProtocol {
// MARK: - Adding to stack
/// Gets the percent modifier. This value is used to help properly calculate percent for stack items when spacing is involved.
private func getPercentModifier() -> CGFloat {
private func getTotalSpace() -> CGFloat {
guard let stackModel = stackModel else { return 0.0 }
var totalSpace: CGFloat = 0.0
var totalViews = 0
var firstMoleculeFound = false
for stackItemModel in stackModel.molecules {
guard !stackItemModel.gone else { continue }
totalViews += 1
let spacing = stackItemModel.spacing ?? stackModel.spacing
if firstMoleculeFound {
totalSpace += spacing
@ -189,11 +188,11 @@ open class Stack<T>: Container where T: StackModelProtocol {
totalSpace += (stackModel.useStackSpacingBeforeFirstItem ? spacing : stackItemModel.spacing ?? 0)
}
}
return (totalViews > 0 ? -(totalSpace / CGFloat(totalViews)) : 0)
return totalSpace
}
/// Adds the stack item view
private func addView(_ view: UIView,_ model: StackItemModelProtocol, percentModifier: CGFloat, lastItem: Bool) {
private func addView(_ view: UIView,_ model: StackItemModelProtocol, totalSpacing: CGFloat, lastItem: Bool) {
guard let stackModel = self.stackModel else { return }
guard !model.gone else {
// Gone views do not show
@ -223,7 +222,9 @@ open class Stack<T>: Container where T: StackModelProtocol {
pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0)
pinView(contentView, toView: view, attribute: .trailing, relation: .equal, priority: .required, constant: 0)
if let percent = model.percent {
view.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0, constant: percentModifier).isActive = true
let multiplier = CGFloat(percent)/100.0
let constant = multiplier * totalSpacing
view.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: multiplier, constant: -constant).isActive = true
}
if lastItem {
pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0)
@ -240,7 +241,9 @@ open class Stack<T>: Container where T: StackModelProtocol {
pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0)
pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0)
if let percent = model.percent {
view.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0, constant: percentModifier).isActive = true
let multiplier = CGFloat(percent)/100.0
let constant = multiplier * totalSpacing
view.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: multiplier, constant: -constant).isActive = true
}
if lastItem {
pinView(contentView, toView: view, attribute: .right, relation: .equal, priority: .required, constant: 0)

View File

@ -27,10 +27,10 @@ import Foundation
ModelRegistry.register(LabelAttributeActionModel.self)
// Buttons
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: PrimaryButton.self, viewModelClass: ButtonModel.self)
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: PillButton.self, viewModelClass: ButtonModel.self)
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: TwoButtonView.self, viewModelClass: TwoButtonViewModel.self)
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Link.self, viewModelClass: LinkModel.self)
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CaretButton.self, viewModelClass: CaretLinkModel.self)
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CaretLink.self, viewModelClass: CaretLinkModel.self)
// Entry Field
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: TextEntryField.self, viewModelClass: TextEntryFieldModel.self)

View File

@ -30,14 +30,14 @@ import Foundation
case screenHeading
case header
case footer
case moleculeStack
case stack
case isAtomicTabs
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
pageType = try typeContainer.decode(String.self, forKey: .pageType)
moleculeStack = try typeContainer.decode(MoleculeStackModel.self, forKey: .moleculeStack)
moleculeStack = try typeContainer.decode(MoleculeStackModel.self, forKey: .stack)
screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading)
isAtomicTabs = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAtomicTabs)
header = try typeContainer.decodeMoleculeIfPresent(codingKey: .header)
@ -47,7 +47,7 @@ import Foundation
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(pageType, forKey: .pageType)
try container.encode(moleculeStack, forKey: .moleculeStack)
try container.encode(moleculeStack, forKey: .stack)
try container.encodeIfPresent(screenHeading, forKey: .screenHeading)
try container.encodeIfPresent(isAtomicTabs, forKey: .isAtomicTabs)
try container.encodeModelIfPresent(header, forKey: .header)

View File

@ -41,7 +41,7 @@ import Foundation
screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading)
isAtomicTabs = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAtomicTabs)
header = try typeContainer.decodeMoleculeIfPresent(codingKey: .header)
header = try typeContainer.decodeMoleculeIfPresent(codingKey: .middle)
middle = try typeContainer.decodeMoleculeIfPresent(codingKey: .middle)
footer = try typeContainer.decodeMoleculeIfPresent(codingKey: .footer)
}