diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 21383a16..72b96ff3 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ 94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; + D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */; }; D224798A2314445E003FCCF9 /* LabelSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22479892314445E003FCCF9 /* LabelSwitch.swift */; }; D224798C231450C8003FCCF9 /* HeadlineBodySwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224798B231450C8003FCCF9 /* HeadlineBodySwitch.swift */; }; D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22479932316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift */; }; @@ -84,6 +85,7 @@ D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = D22D1F45220496A30077CEC0 /* MVMCoreUISwitch.m */; }; D22D1F562204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */; }; + D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; D260D7B122D65BDD007E7233 /* MVMCoreUIPageControl.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */; }; D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -235,6 +237,8 @@ D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */; }; D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; + D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; + D2FB151D23A40F1500C20E10 /* StackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* StackItem.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 */; }; @@ -308,6 +312,7 @@ 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeActionModel.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; + D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraintAxis+Extension.swift"; sourceTree = ""; }; D22479892314445E003FCCF9 /* LabelSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelSwitch.swift; sourceTree = ""; }; D224798B231450C8003FCCF9 /* HeadlineBodySwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodySwitch.swift; sourceTree = ""; }; D22479932316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintExtension.swift; sourceTree = ""; }; @@ -321,6 +326,7 @@ D22D1F45220496A30077CEC0 /* MVMCoreUISwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUISwitch.m; sourceTree = ""; }; D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIStackableViewController.h; sourceTree = ""; }; D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIStackableViewController.m; sourceTree = ""; }; + D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPageControl.h; sourceTree = ""; }; D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIPageControl.m; sourceTree = ""; }; D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = ""; }; @@ -476,6 +482,8 @@ D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = ""; }; D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = ""; }; D2F4DDE52371A4CB00CD28BB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = ""; }; + D2FB151C23A40F1500C20E10 /* StackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItem.swift; sourceTree = ""; }; DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = ""; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; @@ -664,6 +672,7 @@ D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */, D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */, D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */, + D2FB151C23A40F1500C20E10 /* StackItem.swift */, ); path = Items; sourceTree = ""; @@ -773,6 +782,7 @@ 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */, + D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */, ); path = Molecules; sourceTree = ""; @@ -819,6 +829,7 @@ D29DF2B721E7BE79003B2FB9 /* TabBarController */, D29DF2B621E7BE66003B2FB9 /* SplitViewController */, D2B18B93236214AD00A9AEDC /* NavigationController.swift */, + D243859823A16B1800332775 /* Container.swift */, ); path = Containers; sourceTree = ""; @@ -863,6 +874,7 @@ D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */, 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */, 0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */, + D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */, ); path = Utility; sourceTree = ""; @@ -1236,6 +1248,7 @@ DBC4391922442197001AB423 /* DashLine.swift in Sources */, 0AA33B34239813C50067DD0F /* UIColor+Extension.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, + D2FB151D23A40F1500C20E10 /* StackItem.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */, 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */, @@ -1326,6 +1339,7 @@ D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */, D29DF2C721E7BF57003B2FB9 /* MFTabBarInteractor.m in Sources */, D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, + D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */, D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, @@ -1341,6 +1355,7 @@ D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, + D243859923A16B1800332775 /* Container.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */, 01EB369323609801006832FA /* HeaderModel.swift in Sources */, @@ -1372,6 +1387,7 @@ D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */, D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */, D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */, + D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */, 01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */, 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */, 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Buttons/CaretButton.swift b/MVMCoreUI/Atoms/Buttons/CaretButton.swift index b01d2758..6c9de088 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretButton.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretButton.swift @@ -136,7 +136,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI return true } - open func alignment() -> UIStackView.Alignment { + open func horizontalAlignment() -> UIStackView.Alignment { return UIStackView.Alignment.leading; } diff --git a/MVMCoreUI/Atoms/Buttons/MFTextButton.m b/MVMCoreUI/Atoms/Buttons/MFTextButton.m index e1a6c504..a0ee6ca7 100644 --- a/MVMCoreUI/Atoms/Buttons/MFTextButton.m +++ b/MVMCoreUI/Atoms/Buttons/MFTextButton.m @@ -156,7 +156,7 @@ return YES; } -- (UIStackViewAlignment)alignment { +- (UIStackViewAlignment)horizontalAlignment { return UIStackViewAlignmentLeading; } diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index 3b1802e5..2bc9e026 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -664,6 +664,8 @@ } - (void)setAsMolecule { + self.translatesAutoresizingMaskIntoConstraints = false; + [self pinHeight]; [self setAsStandardCustom]; } @@ -713,6 +715,10 @@ [self setWithActionMap:json delegateObject:delegateObject additionalData:additionalData]; } +- (UIStackViewAlignment)horizontalAlignment { + return UIStackViewAlignmentCenter; +} + #pragma mark - Handling Validations - (void)setEnabledByValidity { diff --git a/MVMCoreUI/Atoms/Views/CaretView.swift b/MVMCoreUI/Atoms/Views/CaretView.swift index c02db833..fa34efc1 100644 --- a/MVMCoreUI/Atoms/Views/CaretView.swift +++ b/MVMCoreUI/Atoms/Views/CaretView.swift @@ -152,7 +152,7 @@ extension CaretView: MVMCoreUIViewConstrainingProtocol { return true } - open func alignment() -> UIStackView.Alignment { + open func horizontalAlignment() -> UIStackView.Alignment { return UIStackView.Alignment.leading; } } diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index f148863b..7ce1104e 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -165,7 +165,7 @@ extension CheckboxWithLabelView { layoutIfNeeded() } - override open func alignment() -> UIStackView.Alignment { + override open func horizontalAlignment() -> UIStackView.Alignment { return .leading } diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index ca112b9a..b37e74fb 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -705,7 +705,7 @@ extension Label { return true } - public func alignment() -> UIStackView.Alignment { + public func horizontalAlignment() -> UIStackView.Alignment { return .leading } diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m index 8ec71241..33874a8d 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m @@ -193,7 +193,7 @@ const CGFloat SwitchShakeIntensity = 2; return YES; } -- (UIStackViewAlignment)alignment { +- (UIStackViewAlignment)horizontalAlignment { return UIStackViewAlignmentTrailing; } diff --git a/MVMCoreUI/Atoms/Views/ProgressBar.swift b/MVMCoreUI/Atoms/Views/ProgressBar.swift index 45ce85d6..79e64fcc 100644 --- a/MVMCoreUI/Atoms/Views/ProgressBar.swift +++ b/MVMCoreUI/Atoms/Views/ProgressBar.swift @@ -73,8 +73,9 @@ import Foundation if let thickness = json?.optionalCGFloatForKey("thickness") { self.thickness = thickness } - if let percentage = json?["percent"] as? Float { - progress = percentage/100.0 + // as? Float returns nil, apple defect. + if let percentage = json?["percent"] as? CGFloat { + progress = Float(percentage/100.0) } if let progressColor = json?.optionalStringForKey("progressColor") { progressTintColor = UIColor.mfGet(forHex: progressColor) diff --git a/MVMCoreUI/Containers/Container.swift b/MVMCoreUI/Containers/Container.swift new file mode 100644 index 00000000..58f7c849 --- /dev/null +++ b/MVMCoreUI/Containers/Container.swift @@ -0,0 +1,261 @@ +// +// Container.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 12/11/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +public protocol ContainerModelProtocol: Model { + var horizontalAlignment: UIStackView.Alignment? { get set } + var verticalAlignment: UIStackView.Alignment? { get set } + var useHorizontalMargins: Bool? { get set } + var useVerticalMargins: Bool? { get set } +} + +public class ContainerHelper: NSObject { + var leftConstraint: NSLayoutConstraint? + var topConstraint: NSLayoutConstraint? + var bottomConstraint: NSLayoutConstraint? + var rightConstraint: NSLayoutConstraint? + + var alignCenterHorizontalConstraint: NSLayoutConstraint? + var alignCenterLeftConstraint: NSLayoutConstraint? + var alignCenterRightConstraint: NSLayoutConstraint? + + var alignCenterVerticalConstraint: NSLayoutConstraint? + var alignCenterTopConstraint: NSLayoutConstraint? + var alignCenterBottomConstraint: NSLayoutConstraint? + + var leftLowConstraint: NSLayoutConstraint? + var topLowConstraint: NSLayoutConstraint? + var bottomLowConstraint: NSLayoutConstraint? + var rightLowConstraint: NSLayoutConstraint? + + func constrainView(_ view: UIView) { + guard let margins = view.superview?.layoutMarginsGuide else { return } + leftConstraint = view.leftAnchor.constraint(equalTo: margins.leftAnchor) + leftConstraint?.isActive = true + + topConstraint = view.topAnchor.constraint(equalTo: margins.topAnchor) + topConstraint?.isActive = true + + rightConstraint = margins.rightAnchor.constraint(equalTo: view.rightAnchor) + rightConstraint?.isActive = true + + bottomConstraint = margins.bottomAnchor.constraint(equalTo: view.bottomAnchor) + bottomConstraint?.isActive = true + + alignCenterHorizontalConstraint = view.centerXAnchor.constraint(equalTo: margins.centerXAnchor) + alignCenterLeftConstraint = view.leftAnchor.constraint(greaterThanOrEqualTo: margins.leftAnchor) + alignCenterRightConstraint = margins.rightAnchor.constraint(greaterThanOrEqualTo: view.rightAnchor) + + alignCenterVerticalConstraint = view.centerYAnchor.constraint(equalTo: margins.centerYAnchor) + alignCenterTopConstraint = view.topAnchor.constraint(greaterThanOrEqualTo: margins.topAnchor) + alignCenterBottomConstraint = margins.bottomAnchor.constraint(greaterThanOrEqualTo: view.bottomAnchor) + + leftLowConstraint = view.leftAnchor.constraint(equalTo: margins.leftAnchor) + leftLowConstraint?.priority = UILayoutPriority(rawValue: 200) + leftLowConstraint?.isActive = true + + topLowConstraint = view.topAnchor.constraint(equalTo: margins.topAnchor) + topLowConstraint?.priority = UILayoutPriority(rawValue: 200) + topLowConstraint?.isActive = true + + rightLowConstraint = margins.rightAnchor.constraint(equalTo: view.rightAnchor) + rightLowConstraint?.priority = UILayoutPriority(rawValue: 200) + rightLowConstraint?.isActive = true + + bottomLowConstraint = margins.bottomAnchor.constraint(equalTo: view.bottomAnchor) + bottomLowConstraint?.priority = UILayoutPriority(rawValue: 200) + bottomLowConstraint?.isActive = true + + setAccessibility(view) + } + + func setAccessibility(_ view: UIView) { + guard let superView = view.superview else { return } + superView.isAccessibilityElement = false + if let elements = view.accessibilityElements { + superView.accessibilityElements = elements + } else { + superView.accessibilityElements = [view] + } + } + + func alignHorizontal(_ alignment: UIStackView.Alignment) { + switch alignment { + case .center: + alignCenterHorizontalConstraint?.isActive = true + alignCenterLeftConstraint?.isActive = true + alignCenterRightConstraint?.isActive = true + leftConstraint?.isActive = false + rightConstraint?.isActive = false + case .leading: + alignCenterHorizontalConstraint?.isActive = false + alignCenterLeftConstraint?.isActive = false + alignCenterRightConstraint?.isActive = true + leftConstraint?.isActive = true + rightConstraint?.isActive = false + case .trailing: + alignCenterHorizontalConstraint?.isActive = false + alignCenterLeftConstraint?.isActive = true + alignCenterRightConstraint?.isActive = false + leftConstraint?.isActive = false + rightConstraint?.isActive = true + case .fill: + alignCenterHorizontalConstraint?.isActive = false + alignCenterLeftConstraint?.isActive = false + alignCenterRightConstraint?.isActive = false + leftConstraint?.isActive = true + rightConstraint?.isActive = true + default: break + } + } + + func alignVertical(_ alignment: UIStackView.Alignment) { + switch alignment { + case .center: + alignCenterVerticalConstraint?.isActive = true + alignCenterTopConstraint?.isActive = true + alignCenterBottomConstraint?.isActive = true + topConstraint?.isActive = false + bottomConstraint?.isActive = false + case .leading: + alignCenterVerticalConstraint?.isActive = false + alignCenterTopConstraint?.isActive = false + alignCenterBottomConstraint?.isActive = true + topConstraint?.isActive = true + bottomConstraint?.isActive = false + case .trailing: + alignCenterVerticalConstraint?.isActive = false + alignCenterTopConstraint?.isActive = true + alignCenterBottomConstraint?.isActive = false + topConstraint?.isActive = false + bottomConstraint?.isActive = true + case .fill: + alignCenterVerticalConstraint?.isActive = false + alignCenterTopConstraint?.isActive = false + alignCenterBottomConstraint?.isActive = false + topConstraint?.isActive = true + bottomConstraint?.isActive = true + default: break + } + } + + func set(with model: ContainerModelProtocol) { + if let horizontalAlignment = model.horizontalAlignment { + alignHorizontal(horizontalAlignment) + } + if let verticalAlignment = model.verticalAlignment { + alignVertical(verticalAlignment) + } + } + + static func getAlignment(for string: String) -> UIStackView.Alignment? { + switch string { + case "leading": + return .leading + case "trailing": + return .trailing + case "center": + return .center + case "fill": + return .fill + default: + return nil + } + } + + static func getAlignmentString(for alignment: UIStackView.Alignment?) -> String? { + switch alignment { + case .leading: + return "leading" + case .trailing: + return "trailing" + case .center: + return "center" + case .fill: + return "fill" + default: + return nil + } + } + + func set(with JSON: [AnyHashable: Any]?, for contained: UIView) { + if let horizontalAlignmentString = JSON?.optionalStringForKey("horizontalAlignment"), let alignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) ?? (contained as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() { + alignHorizontal(alignment) + } else if let alignment = (contained as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() { + alignHorizontal(alignment) + } + + if let verticalAlignmentString = JSON?.optionalStringForKey("verticalAlignment"), let alignment = ContainerHelper.getAlignment(for: verticalAlignmentString) ?? (contained as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() { + alignVertical(alignment) + } else if let alignment = (contained as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() { + alignVertical(alignment) + } + } +} + +open class Container: View { + var view: UIView? + let containerHelper = ContainerHelper() + var containerModel: ContainerModelProtocol? { + get { return model as? ContainerModelProtocol } + } + var topMarginPadding: CGFloat = 0 + var bottomMarginPadding: CGFloat = 0 + + override open func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + guard let containerModel = model as? ContainerModelProtocol else { return } + containerHelper.set(with: containerModel) + } +} + +// MARK: - MVMCoreViewProtocol +public extension Container { + override func updateView(_ size: CGFloat) { + super.updateView(size) + (view as? MVMCoreViewProtocol)?.updateView(size) + MFStyler.setMarginsFor(self, size: size, defaultHorizontal: containerModel?.useHorizontalMargins ?? true, top: containerModel?.useHorizontalMargins ?? true ? topMarginPadding : 0, bottom: containerModel?.useHorizontalMargins ?? true ? bottomMarginPadding : 0) + } + + /// Will be called only once. + override func setupView() { + super.setupView() + backgroundColor = .clear + } + + func addAndContain(_ view: UIView) { + view.translatesAutoresizingMaskIntoConstraints = false + addSubview(view) + containerHelper.constrainView(view) + self.view = view + } + + convenience init(andContain view: UIView) { + self.init() + addAndContain(view) + } +} + +// MARK: - MVMCoreUIMoleculeViewProtocol +public extension Container { + override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + guard let view = view else { return } + containerHelper.set(with: json, for: view) + } + + override func reset() { + super.reset() + (view as? MVMCoreUIMoleculeViewProtocol)?.reset?() + } + + func setAsMolecule() { + (view as? MVMCoreUIMoleculeViewProtocol)?.setAsMolecule?() + } +} diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m index 54358ebe..40fa987f 100644 --- a/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m @@ -54,7 +54,7 @@ static const CGFloat CheckBoxHeightWidth = 18.0; return YES; } -- (UIStackViewAlignment)alignment { +- (UIStackViewAlignment)horizontalAlignment { return UIStackViewAlignmentLeading; } diff --git a/MVMCoreUI/Models/Molecules/MoleculeStackModel.swift b/MVMCoreUI/Models/Molecules/MoleculeStackModel.swift index 1ca9b9da..777fcbb1 100644 --- a/MVMCoreUI/Models/Molecules/MoleculeStackModel.swift +++ b/MVMCoreUI/Models/Molecules/MoleculeStackModel.swift @@ -8,15 +8,19 @@ import Foundation -@objcMembers public class MoleculeStackModel: MoleculeProtocol { - +@objcMembers public class MoleculeStackModel: ContainerModelProtocol, MoleculeProtocol { public static var identifier: String = "moleculeStack" public var backgroundColor: String? - public var molecules: [MoleculeStackItemModel]? - public var axis: String? + public var molecules: [StackItemModel] + public var axis: NSLayoutConstraint.Axis? = .vertical + public var spacing: CGFloat? + + public var horizontalAlignment: UIStackView.Alignment? + public var verticalAlignment: UIStackView.Alignment? + public var useHorizontalMargins: Bool? + public var useVerticalMargins: Bool? - public init(axis: String?, molecules: [MoleculeStackItemModel]?) { - self.axis = axis + public init(molecules: [StackItemModel]) { self.molecules = molecules } @@ -24,18 +28,23 @@ import Foundation case moleculeName case molecules case axis + case spacing } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.molecules = try typeContainer.decode([MoleculeStackItemModel].self, forKey: .molecules) - self.axis = try typeContainer.decodeIfPresent(String.self, forKey: .axis) + molecules = try typeContainer.decodeMolecules(codingKey: .molecules) as! [StackItemModel] + if let axisString = try typeContainer.decodeIfPresent(String.self, forKey: .axis), let optionalAxis = NSLayoutConstraint.Axis(rawValue: axisString) { + axis = optionalAxis + } + spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(molecules, forKey: .molecules) - try container.encode(axis, forKey: .axis) + try container.encodeIfPresent(axis?.rawValueString, forKey: .axis) + try container.encodeIfPresent(spacing, forKey: .spacing) } } diff --git a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift index 75249f7d..35b3ed04 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift @@ -11,7 +11,8 @@ import UIKit open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol { open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? open var json: [AnyHashable: Any]? - + public let containerHelper = ContainerHelper() + // In updateView, will set padding to default. open var updateViewHorizontalDefaults = true open var updateViewVerticalDefaults = true @@ -91,21 +92,19 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi return } if molecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { + if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: false) { contentView.insertSubview(moleculeView, at: 0) - NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: true).values)) + containerHelper.constrainView(moleculeView) molecule = moleculeView } } else { molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) } - // This molecule will handle spacing by default. - if let castView = molecule as? MVMCoreUIViewConstrainingProtocol { - castView.shouldSetHorizontalMargins?(false) - castView.shouldSetVerticalMargins?(false) - } + + guard let molecule = molecule else { return } + containerHelper.set(with: json, for: molecule) - accessibilityElements = molecule?.subviews + accessibilityElements = molecule.subviews } public func reset() { diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 4769b7e5..7cd3a900 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -11,11 +11,20 @@ import UIKit @objcMembers open class MoleculeTableViewCell: TableViewCell { // MARK: - MVMCoreUIMoleculeViewProtocol - public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) + { + guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule) else { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + return + } + if molecule == nil { + if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: false) { + addMolecule(moleculeView) + } + } else { + molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) + } super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard molecule == nil, let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule), let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) else { return } - addMolecule(moleculeView) } public override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { diff --git a/MVMCoreUI/Molecules/Items/StackItem.swift b/MVMCoreUI/Molecules/Items/StackItem.swift new file mode 100644 index 00000000..f976667d --- /dev/null +++ b/MVMCoreUI/Molecules/Items/StackItem.swift @@ -0,0 +1,88 @@ +// +// StackItem.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 12/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class StackItemModel: ContainerModelProtocol, MoleculeProtocol { + public static var identifier: String = "stackItem" + public var backgroundColor: String? + public var view: StackItem? + + public var molecule: MoleculeProtocol + public var spacing: CGFloat? = 16 + public var percentage: Int? = 0 + public var verticalAlignment: UIStackView.Alignment? + public var horizontalAlignment: UIStackView.Alignment? + public var useHorizontalMargins: Bool? = false + public var useVerticalMargins: Bool? = false + public var gone: Bool? = false + + enum CodingKeys: String, CodingKey { + case molecule + case spacing + case percentage + case verticalAlignment + case horizontalAlignment + case useHorizontalMargins + case useVerticalMargins + case gone + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + molecule = try typeContainer.decodeMolecule(codingKey: .molecule) + spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) + percentage = try typeContainer.decodeIfPresent(Int.self, forKey: .percentage) + if let verticalAlignmentString = try typeContainer.decodeIfPresent(String.self, forKey: .verticalAlignment) { + verticalAlignment = ContainerHelper.getAlignment(for: verticalAlignmentString) + } + if let horizontalAlignmentString = try typeContainer.decodeIfPresent(String.self, forKey: .horizontalAlignment) { + horizontalAlignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) + } + useVerticalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalMargins) + useHorizontalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useHorizontalMargins) + gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModel(molecule, forKey: .molecule) + try container.encodeIfPresent(spacing, forKey: .spacing) + try container.encodeIfPresent(percentage, forKey: .percentage) + try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: verticalAlignment), forKey: .verticalAlignment) + try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: horizontalAlignment), forKey: .horizontalAlignment) + try container.encodeIfPresent(useVerticalMargins, forKey: .useVerticalMargins) + try container.encodeIfPresent(useHorizontalMargins, forKey: .useHorizontalMargins) + try container.encodeIfPresent(gone, forKey: .gone) + } + + func update(with json: [AnyHashable: Any]?) { + gone = json?.boolForKey("gone") ?? (json == nil) + spacing = json?.optionalCGFloatForKey("spacing") + percentage = json?["percent"] as? Int + if let horizontalAlignmentString = json?.optionalStringForKey("horizontalAlignment") { + horizontalAlignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) + } else { + horizontalAlignment = nil + } + + if let verticalAlignmentString = json?.optionalStringForKey("verticalAlignment") { + verticalAlignment = ContainerHelper.getAlignment(for: verticalAlignmentString) + } else { + verticalAlignment = nil + } + + useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") ?? false + useVerticalMargins = json?.optionalBoolForKey("useVerticalMargins") ?? false + } +} + +open class StackItem: MoleculeContainer { + + +} diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index 13faeb42..9cf77ae1 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -11,7 +11,8 @@ import UIKit @objcMembers open class TableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol { open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? open var json: [AnyHashable: Any]? - + public let containerHelper = ContainerHelper() + // In updateView, will set padding to default. open var updateViewHorizontalDefaults = true @@ -48,6 +49,8 @@ import UIKit styleStandard() case "header": styleHeader() + case "sectionFooter": + styleFooter() case "none": styleNone() default: break @@ -68,6 +71,13 @@ import UIKit bottomSeparatorView?.style = .thin } + open func styleFooter() { + topMarginPadding = 24 + bottomMarginPadding = 0 + topSeparatorView?.style = .none + bottomSeparatorView?.style = .none + } + open func styleNone() { topMarginPadding = 0 bottomMarginPadding = 0 @@ -78,14 +88,7 @@ import UIKit /// Adds the molecule to the view. open func addMolecule(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { contentView.addSubview(molecule) - let standardConstraints = (molecule as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true - NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: molecule, useMargins: standardConstraints).values)) - - // This molecule will by default handle margins. - if let castView = molecule as? MVMCoreUIViewConstrainingProtocol { - castView.shouldSetHorizontalMargins?(false) - castView.shouldSetVerticalMargins?(false) - } + containerHelper.constrainView(molecule) self.molecule = molecule } @@ -176,14 +179,8 @@ import UIKit bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData) } - guard let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return } - molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) - - // This molecule will by default handle margins. - if let castView = molecule as? MVMCoreUIViewConstrainingProtocol { - castView.shouldSetHorizontalMargins?(false) - castView.shouldSetVerticalMargins?(false) - } + guard let molecule = molecule else { return } + containerHelper.set(with: json, for: molecule) } public func reset() { @@ -237,11 +234,13 @@ import UIKit var queue = [UIView]() - for view in views { + // Reversed the array to first check views at the front. + for view in views.reversed() { // Only one Label will have a hero in a table cell. if let label = view as? Label, label.hero != nil { return label } + queue.append(contentsOf: view.subviews) } diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index 77acc007..3c2277c9 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -8,15 +8,58 @@ import UIKit -open class ModuleMolecule: ViewConstrainingView { +open class ModuleMoleculeModel: ContainerModelProtocol { + public static var identifier: String = "moduleMolecule" - open var moduleMolecule: (UIView & MVMCoreUIMoleculeViewProtocol)? - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - moduleMolecule?.updateView(size) + public var molecule: MoleculeProtocol? + public var moduleName: String + public var horizontalAlignment: UIStackView.Alignment? = .fill + public var verticalAlignment: UIStackView.Alignment? = .fill + public var useHorizontalMargins: Bool? = false + public var useVerticalMargins: Bool? = false + + enum CodingKeys: String, CodingKey { + case molecule + case moduleName + case horizontalAlignment + case verticalAlignment + case useHorizontalMargins + case useVerticalMargins } + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + moduleName = try typeContainer.decode(String.self, forKey:.moduleName) + if let verticalAlignmentString = try typeContainer.decodeIfPresent(String.self, forKey: .verticalAlignment) { + verticalAlignment = ContainerHelper.getAlignment(for: verticalAlignmentString) + } + if let horizontalAlignmentString = try typeContainer.decodeIfPresent(String.self, forKey: .horizontalAlignment) { + horizontalAlignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) + } + useVerticalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalMargins) + useHorizontalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useHorizontalMargins) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moduleName, forKey: .moduleName) + try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: verticalAlignment), forKey: .verticalAlignment) + try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: horizontalAlignment), forKey: .horizontalAlignment) + try container.encodeIfPresent(useVerticalMargins, forKey: .useVerticalMargins) + try container.encodeIfPresent(useHorizontalMargins, forKey: .useHorizontalMargins) + } +} + +open class ModuleMolecule: Container { + var moduleMoleculeModel: ModuleMoleculeModel? { + get { return model as? ModuleMoleculeModel } + } + + open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + #warning("need to change getter to get moduleModel instead to use.") + super.setWithModel(model, delegateObject, additionalData) + } + // MARK: - MVMCoreUIMoleculeViewProtocol open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) @@ -26,35 +69,16 @@ open class ModuleMolecule: ViewConstrainingView { return } - if moduleMolecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: module, delegateObject: delegateObject, constrainIfNeeded: true) { - addSubview(moleculeView) - NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: false).values)) - moduleMolecule = moleculeView - - isAccessibilityElement = false - if moleculeView.accessibilityElements != nil { - accessibilityElements = moleculeView.accessibilityElements - } else { - accessibilityElements = [moleculeView] - } + if view == nil { + if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: module, delegateObject: delegateObject, constrainIfNeeded: false) { + addAndContain(moleculeView) } } else { - moduleMolecule?.setWithJSON(module, delegateObject: delegateObject, additionalData: additionalData) + (view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(module, delegateObject: delegateObject, additionalData: additionalData) } } - open override func setAsMolecule() { - super.setAsMolecule() - moduleMolecule?.setAsMolecule?() - } - - open override func reset() { - super.reset() - moduleMolecule?.reset?() - } - - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let moduleName = json?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else { // Critical error return 0 @@ -62,7 +86,7 @@ open class ModuleMolecule: ViewConstrainingView { return MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.estimatedHeight?(forRow: module, delegateObject: delegateObject) ?? 0 } - public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + public class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let moduleName = molecule?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else { // Critical error return "moduleMolecule<>" @@ -70,7 +94,7 @@ open class ModuleMolecule: ViewConstrainingView { return "moduleMolecule<" + (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.name?(forReuse: module, delegateObject: delegateObject) ?? module.stringForkey(KeyMoleculeName)) + ">" } - public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { let moduleName = json?.optionalStringForKey("moduleName") if moduleName == nil || delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) == nil { if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) { @@ -83,25 +107,4 @@ open class ModuleMolecule: ViewConstrainingView { } return nil } - - // MARK: - MVMCoreUIViewConstrainingProtocol - open override func useStandardConstraints() -> Bool { - return (moduleMolecule as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true - } - - open override func alignHorizontal(_ alignment: UIStackView.Alignment) { - (moduleMolecule as? MVMCoreUIViewConstrainingProtocol)?.alignHorizontal?(alignment) - } - - open override func alignVertical(_ alignment: UIStackView.Alignment) { - (moduleMolecule as? MVMCoreUIViewConstrainingProtocol)?.alignVertical?(alignment) - } - - open override func shouldSetHorizontalMargins(_ shouldSet: Bool) { - (moduleMolecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(shouldSet) - } - - open override func shouldSetVerticalMargins(_ shouldSet: Bool) { - (moduleMolecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(shouldSet) - } } diff --git a/MVMCoreUI/Molecules/MoleculeContainer.swift b/MVMCoreUI/Molecules/MoleculeContainer.swift new file mode 100644 index 00000000..14a5bb4a --- /dev/null +++ b/MVMCoreUI/Molecules/MoleculeContainer.swift @@ -0,0 +1,27 @@ +// +// MoleculeContainer.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 12/12/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class MoleculeContainer: Container { + + override public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule) else { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + return + } + if view == nil { + if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: false) { + addAndContain(molecule) + } + } else { + (view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) + } + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + } +} diff --git a/MVMCoreUI/Molecules/StandardFooterView.swift b/MVMCoreUI/Molecules/StandardFooterView.swift index 7c9c9282..a83611cc 100644 --- a/MVMCoreUI/Molecules/StandardFooterView.swift +++ b/MVMCoreUI/Molecules/StandardFooterView.swift @@ -8,28 +8,23 @@ import UIKit -open class StandardFooterView: ViewConstrainingView { +open class StandardFooterView: MoleculeContainer { open override func setupView() { super.setupView() topMarginPadding = PaddingDefaultVerticalSpacing bottomMarginPadding = PaddingDefaultVerticalSpacing - shouldSetupMoleculeFromJSON = true - updateViewVerticalDefaults = true - updateViewHorizontalDefaults = true } - open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - // This molecule will by default handle margins. - (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(false) - (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false) - } - - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing } return 42 } + + open override func reset() { + super.reset() + topMarginPadding = PaddingDefaultVerticalSpacing + bottomMarginPadding = PaddingDefaultVerticalSpacing + } } diff --git a/MVMCoreUI/Molecules/StandardHeaderView.swift b/MVMCoreUI/Molecules/StandardHeaderView.swift index e6d00efa..a1264d81 100644 --- a/MVMCoreUI/Molecules/StandardHeaderView.swift +++ b/MVMCoreUI/Molecules/StandardHeaderView.swift @@ -8,7 +8,7 @@ import UIKit -public class StandardHeaderView: ViewConstrainingView, ModelMoleculeViewProtocol { +public class StandardHeaderView: MoleculeContainer { var line: Line? // MARK: - MVMCoreViewProtocol @@ -19,9 +19,6 @@ public class StandardHeaderView: ViewConstrainingView, ModelMoleculeViewProtocol public override func setupView() { super.setupView() - shouldSetupMoleculeFromJSON = true - updateViewVerticalDefaults = true - updateViewHorizontalDefaults = true topMarginPadding = PaddingDefaultVerticalSpacing bottomMarginPadding = PaddingDefaultVerticalSpacing @@ -38,26 +35,13 @@ public class StandardHeaderView: ViewConstrainingView, ModelMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - // This molecule will by default handle margins. - (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(false) - (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false) - if let separatorJSON = json?.optionalDictionaryForKey("separator") { line?.setWithJSON(separatorJSON, delegateObject: delegateObject, additionalData: additionalData) } } - open func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { - //TODO: Need to create setWithModel in ViewConstraining View - - #warning("This below call should be repaced with super.setWithModel once we get rid of ViewConstrainingView.") - //TODO: This below call should be repaced with super.setWithModel once we get rid of ViewConstrainingView. - setUpWithModel(model, delegateObject, additionalData) - - // This molecule will by default handle margins. - (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(false) - (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false) + open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) guard let headerModel = model as? HeaderModel else { return @@ -70,12 +54,12 @@ public class StandardHeaderView: ViewConstrainingView, ModelMoleculeViewProtocol open override func reset() { super.reset() + line?.style = .heavy topMarginPadding = PaddingDefaultVerticalSpacing bottomMarginPadding = PaddingDefaultVerticalSpacing - line?.style = .heavy } - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index a7d96a34..869bd23a 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -22,15 +22,14 @@ import UIKit return } stack.spacing = 0 - stack.updateViewHorizontalDefaults = false addSubview(stack) pinView(toSuperView: stack) - stack.addStackItem(StackItem(with: eyebrow), lastItem: false) - stack.addStackItem(StackItem(with: headline), lastItem: false) - stack.addStackItem(StackItem(with: body), lastItem: false) + stack.addStackItem(StackItemModel(with: StackItem(andContain: eyebrow)), lastItem: false) + stack.addStackItem(StackItemModel(with: StackItem(andContain: headline)), lastItem: false) + stack.addStackItem(StackItemModel(with: StackItem(andContain: body)), lastItem: false) // To visually take into account the extra padding in the intrinsic content of a button. - let stackItem = StackItem(with: link) + let stackItem = StackItemModel(with: StackItem(andContain: link)) stackItem.spacing = -6 stack.addStackItem(stackItem, lastItem: true) } @@ -58,7 +57,6 @@ import UIKit super.reset() stack.reset() stack.spacing = 0 - stack.updateViewHorizontalDefaults = false eyebrow.styleB3(true) headline.styleB1(true) body.styleB2(true) diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index dc7cc81e..31177540 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -8,76 +8,17 @@ import UIKit -public class StackItem { - var view: UIView - var spacing: CGFloat? - var percentage: Int? - var verticalAlignment: UIStackView.Alignment? - var horizontalAlignment: UIStackView.Alignment? - var gone = false - - init(with view: UIView) { - self.view = view - } - - init(with view: UIView, json: [AnyHashable: Any]?) { - self.view = view - update(with: json) - } - - func update(with json: [AnyHashable: Any]?) { - gone = json?.boolForKey("gone") ?? (json == nil) - spacing = json?.optionalCGFloatForKey("spacing") - percentage = json?["percent"] as? Int - if let alignment = json?.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"verticalAlignment"]) { - verticalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill) - } else { - verticalAlignment = nil - } - if let alignment = json?.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"horizontalAlignment"]) { - horizontalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill) - } else { - horizontalAlignment = nil - } - } -} - -public class MoleculeStackView: ViewConstrainingView { +open class MoleculeStackView: Container { var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() - var items: [StackItem] = [] var useStackSpacingBeforeFirstItem = false - - private var moleculesShouldSetHorizontalMargins = false - private var moleculesShouldSetVerticalMargins = false - - /// For setting the direction of the stack - var axis: NSLayoutConstraint.Axis = .vertical { - didSet { - if axis != oldValue { - restack() - } - } + var stackModel: MoleculeStackModel? { + get { return model as? MoleculeStackModel } } - /// The spacing to use between each item in the stack. - var spacing: CGFloat = 16 { - didSet { - if spacing != oldValue { - restack() - } - } - } + var moleculesShouldSetHorizontalMargins = false + var moleculesShouldSetVerticalMargins = false // MARK: - Helpers - public func setAxisWithJSON(_ json: [AnyHashable: Any]?) { - switch json?.optionalStringForKey("axis") { - case "horizontal": - axis = .horizontal - default: - axis = .vertical - } - } - public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) { let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant) constraint.priority = priority @@ -86,17 +27,23 @@ public class MoleculeStackView: ViewConstrainingView { /// Restacks the existing items. func restack() { - setWithStackItems(items) + guard let stackItems = stackModel?.molecules else { return } + setWithStackItems(stackItems) } /// Removes all stack items views from the view. func removeAllItemViews() { - for item in items { - item.view.removeFromSuperview() + guard let stackItems = stackModel?.molecules else { return } + for item in stackItems { + item.view?.removeFromSuperview() } } // MARK: - Inits + public override init() { + super.init() + } + public override init(frame: CGRect) { super.init(frame: frame) } @@ -117,19 +64,21 @@ public class MoleculeStackView: ViewConstrainingView { return } MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) - updateViewHorizontalDefaults = true translatesAutoresizingMaskIntoConstraints = false backgroundColor = .clear addSubview(contentView) - pinView(toSuperView: contentView) + containerHelper.constrainView(contentView) contentView.setContentHuggingPriority(.defaultHigh, for: .vertical) contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal) } public override func updateView(_ size: CGFloat) { super.updateView(size) - for item in items { - (item.view as? MVMCoreViewProtocol)?.updateView(size) + directionalLayoutMargins.leading = 0 + directionalLayoutMargins.trailing = 0 + guard let stackItems = stackModel?.molecules else { return } + for item in stackItems { + item.view?.updateView(size) } } @@ -137,54 +86,68 @@ public class MoleculeStackView: ViewConstrainingView { public override func reset() { super.reset() backgroundColor = .clear - updateViewHorizontalDefaults = true - for item in items { - if let view = item.view as? MVMCoreUIMoleculeViewProtocol { - view.reset?() - } + guard let stackItems = stackModel?.molecules else { return } + for item in stackItems { + item.view?.reset() } } + public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + restack() + stackModel?.useHorizontalMargins = moleculesShouldSetHorizontalMargins + stackModel?.useVerticalMargins = moleculesShouldSetVerticalMargins + } + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if model == nil { + model = MoleculeStackModel(molecules: []) + } + let previousJSON = self.json super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) removeAllItemViews() // If the items in the stack are the same, just update previous items instead of re-allocating. - var items: [StackItem]? + var items: [StackItemModel]? if MoleculeStackView.name(forReuse: previousJSON, delegateObject: delegateObject) == MoleculeStackView.name(forReuse: json, delegateObject: delegateObject) { - items = self.items + items = stackModel?.molecules } - self.items = [] + stackModel?.molecules = [] guard let molecules = json?.arrayForKey(KeyMolecules) as? [[String: Any]] else { return } // Sets the stack attributes + stackModel?.axis setAxisWithJSON(json) spacing = json?.optionalCGFloatForKey("spacing") ?? 16 // Adds the molecules and sets the json. for (index, map) in molecules.enumerated() { - if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule) { - var view: UIView? - if let item = items?[index] { - item.update(with: map) - view = item.view - (view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: nil) - addStackItem(item, lastItem: index == molecules.count - 1) - } else if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { - view = molecule - addStackItem(StackItem(with: molecule, json: map), lastItem: index == molecules.count - 1) - } - (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(moleculesShouldSetHorizontalMargins) - (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(moleculesShouldSetVerticalMargins) + var view: UIView? + var stackItemModel: StackItemModel + if let item = items?[index] { + stackItemModel = item + item.update(with: map) + view = item.view + (view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(map, delegateObject: delegateObject, additionalData: nil) + addStackItem(item, lastItem: index == molecules.count - 1) + } else { + let stackItem = StackItem() + stackItem.setWithJSON(map, delegateObject: delegateObject, additionalData: additionalData) + view = stackItem + stackItemModel = StackItemModel(with: stackItem, json: map) + addStackItem(stackItemModel, lastItem: index == molecules.count - 1) } + + stackItemModel.useHorizontalMargins = moleculesShouldSetHorizontalMargins + stackItemModel.useVerticalMargins = moleculesShouldSetVerticalMargins } } - public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + public class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { // This will aggregate names of molecules to make an id. guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else { return "stack<>" @@ -199,7 +162,7 @@ public class MoleculeStackView: ViewConstrainingView { return name } - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return 0 } @@ -221,7 +184,7 @@ public class MoleculeStackView: ViewConstrainingView { return estimatedHeight } - public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return nil } @@ -237,11 +200,11 @@ public class MoleculeStackView: ViewConstrainingView { // MARK: - Adding to stack /// Adds the view to the stack. func addView(_ view: UIView, lastItem: Bool) { - addStackItem(StackItem(with: view), lastItem: lastItem) + addStackItem(StackItemModel(with: StackItem(andContain: view)), lastItem: lastItem) } /// Adds the stack item to the stack. - func addStackItem(_ stackItem: StackItem, lastItem: Bool) { + func addStackItem(_ stackItem: StackItemModel, lastItem: Bool) { guard !stackItem.gone else { items.append(stackItem) return @@ -251,12 +214,11 @@ public class MoleculeStackView: ViewConstrainingView { view.translatesAutoresizingMaskIntoConstraints = false let spacing = stackItem.spacing ?? self.spacing - if let view = view as? MVMCoreUIViewConstrainingProtocol { - let verticalAlignment = stackItem.verticalAlignment ?? (stackItem.percentage == nil && axis == .vertical ? .fill : (axis == .vertical ? .leading : .center)) - let horizontalAlignment = stackItem.horizontalAlignment ?? view.alignment?() ?? (axis == .vertical || stackItem.percentage == nil ? .fill : .leading) - view.alignHorizontal?(horizontalAlignment) - view.alignVertical?(verticalAlignment) - } + let verticalAlignment = stackItem.verticalAlignment ?? (stackItem.percentage == nil && axis == .vertical ? .fill : (axis == .vertical ? .leading : .center)) + let horizontalAlignment = stackItem.horizontalAlignment ?? (view.view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (axis == .vertical || stackItem.percentage == nil ? .fill : .leading) + view.containerHelper.alignHorizontal(horizontalAlignment) + view.containerHelper.alignVertical(verticalAlignment) + let first = items.first { !$0.gone } == nil if axis == .vertical { if first { @@ -295,10 +257,10 @@ public class MoleculeStackView: ViewConstrainingView { items.append(stackItem) } - func setWithStackItems(_ items: [StackItem]) { + func setWithStackItems(_ items: [StackItemModel]) { removeAllItemViews() self.items.removeAll() - var previousPresentItem: StackItem? = nil + var previousPresentItem: StackItemModel? = nil for item in items { if !item.gone { previousPresentItem = item diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index a7b8e586..378332b6 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -28,7 +28,7 @@ mapping = [@{ @"label": Label.class, @"line": Line.class, - @"button": ButtonView.class, + @"button": PrimaryButton.class, @"textButton": MFTextButton.class, @"header": StandardHeaderView.class, @"moleculeStack": MoleculeStackView.class, diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h b/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h index afea0ef6..a2ac2340 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h @@ -16,7 +16,13 @@ - (BOOL)needsToBeConstrained; /// The alignment if constrained. -- (UIStackViewAlignment)alignment; +- (UIStackViewAlignment)alignment __deprecated; + +/// The alignment if constrained. +- (UIStackViewAlignment)horizontalAlignment; + +/// The alignment if constrained. +- (UIStackViewAlignment)verticalAlignment; /// Can be used to override any standard constraints that may be added. - (BOOL)useStandardConstraints; diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index dad4d5ef..ee4af899 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -46,6 +46,7 @@ open class MoleculeStackTemplate: ThreeLayerViewController { } let stack = MoleculeStackView(frame: .zero) stack.useStackSpacingBeforeFirstItem = true + stack.moleculesShouldSetHorizontalMargins = true stack.setWithJSON(moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) return stack } diff --git a/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift b/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift new file mode 100644 index 00000000..05d13bcd --- /dev/null +++ b/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift @@ -0,0 +1,56 @@ +// +// NSLayoutConstraintAxis+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 12/20/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + +/** + When using this class in codable for a String value from server. + + Example use case.... + + var axis: NSLayoutConstraint.Axis + + enum CodingKeys: String, CodingKey { + case axis + } + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let word = try container.decode(String.self, forKey: .axis) + axis = NSLayoutConstraint.Axis(rawValue: axis)! + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(axis.rawValueString, forKey: .axis) + } + */ +extension NSLayoutConstraint.Axis: RawRepresentable { + + init?(rawValue: String) { + switch rawValue { + case "horizontal": + self = .horizontal + case "vertical": + self = .vertical + default: + return nil + } + } + + var rawValueString: String { + switch self { + case .horizontal: + return "horizontal" + case .vertical: + return "vertical" + @unknown default: + return "" + } + } +}