From b00b43eab0b76ca50b072f9ab1e5a860756dd896 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 22 Apr 2019 13:14:22 -0400 Subject: [PATCH 1/7] Helper functions Molecule List. Back to view instead of margins for stack due to background color issues. Standard Header View to margin for internal use. --- MVMCoreUI.xcodeproj/project.pbxproj | 16 ++ .../Views/ConstrainingMoleculeView.swift | 55 ++++ MVMCoreUI/Atoms/Views/MFLabel.m | 4 + MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 2 + MVMCoreUI/BaseControllers/MFViewController.m | 4 + .../ThreeLayerTableViewController.swift | 255 ++++++++++++++++++ .../ThreeLayerViewController.swift | 2 +- .../Molecules/MVMCoreUIMoleculeViewProtocol.h | 3 + MVMCoreUI/Molecules/MoleculeStackView.swift | 7 +- .../Molecules/MoleculeTableViewCell.swift | 34 +++ MVMCoreUI/Molecules/StandardFooterView.swift | 18 +- .../MVMCoreUIMoleculeMappingObject.m | 5 +- .../MVMCoreUIViewControllerMappingObject.m | 3 +- .../Templates/MoleculeListTemplate.swift | 57 ++++ .../Templates/MoleculeStackTemplate.swift | 2 +- 15 files changed, 444 insertions(+), 23 deletions(-) create mode 100644 MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift create mode 100644 MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift create mode 100644 MVMCoreUI/Molecules/MoleculeTableViewCell.swift create mode 100644 MVMCoreUI/Templates/MoleculeListTemplate.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 1f3e1d53..6cb9dc9e 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -147,6 +147,7 @@ D29DF32521ED0DA2003B2FB9 /* TextButtonView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF32321ED0DA2003B2FB9 /* TextButtonView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF32C21EE8736003B2FB9 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D29DF32821EE8736003B2FB9 /* Localizable.strings */; }; D29DF32E21EE8C3D003B2FB9 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D29DF32D21EE8C3D003B2FB9 /* Media.xcassets */; }; + D2A421BF226A14F100A05A88 /* ConstrainingMoleculeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A421BE226A14F100A05A88 /* ConstrainingMoleculeView.swift */; }; D2A514582211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */; }; D2A5145D2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -158,6 +159,9 @@ D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */; }; + D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */; }; + D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; + D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; 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 */; }; @@ -308,6 +312,7 @@ D29DF32A21EE8736003B2FB9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; D29DF32B21EE8736003B2FB9 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Localizable.strings"; sourceTree = ""; }; D29DF32D21EE8C3D003B2FB9 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; + D2A421BE226A14F100A05A88 /* ConstrainingMoleculeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstrainingMoleculeView.swift; sourceTree = ""; }; D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeMappingObject.h; sourceTree = ""; }; D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIMoleculeMappingObject.m; sourceTree = ""; }; D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeViewProtocol.h; sourceTree = ""; }; @@ -319,6 +324,9 @@ D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIDelegateObject.swift; sourceTree = ""; }; + D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = ""; }; + D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = ""; }; + D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = ""; }; DBC4391A224421A0001AB423 /* CaretButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretButton.swift; sourceTree = ""; }; @@ -407,6 +415,7 @@ 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */, D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */, D2A514622213643100345BFB /* MoleculeStackCenteredTemplate.swift */, + D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */, ); path = Templates; sourceTree = ""; @@ -442,6 +451,7 @@ D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */, D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */, D274CA322236A78900B01B62 /* StandardFooterView.swift */, + D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */, ); path = Molecules; sourceTree = ""; @@ -462,6 +472,7 @@ D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */, D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */, D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */, + D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */, ); path = BaseControllers; sourceTree = ""; @@ -568,6 +579,7 @@ D29DF28621E7AC2B003B2FB9 /* MFLabel.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, D29DF31F21ED0CBA003B2FB9 /* LabelView.m */, + D2A421BE226A14F100A05A88 /* ConstrainingMoleculeView.swift */, D29DF28721E7AC2B003B2FB9 /* ViewConstrainingView.h */, D29DF28821E7AC2B003B2FB9 /* ViewConstrainingView.m */, D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */, @@ -868,6 +880,7 @@ D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, + D2A421BF226A14F100A05A88 /* ConstrainingMoleculeView.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */, @@ -897,6 +910,7 @@ D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, + D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, D29DF18121E69E50003B2FB9 /* MFView.m in Sources */, D29DF18321E69E54003B2FB9 /* SeparatorView.m in Sources */, @@ -913,6 +927,7 @@ D29DF2C721E7BF57003B2FB9 /* MFTabBarInteractor.m in Sources */, D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, + D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, @@ -920,6 +935,7 @@ 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */, + D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */, D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift b/MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift new file mode 100644 index 00000000..22e8e09d --- /dev/null +++ b/MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift @@ -0,0 +1,55 @@ +// +// ConstrainingMoleculeView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/19/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers open class ConstrainingMoleculeView: MFView { + var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + + public init(withMolecule molecule: UIView & MVMCoreUIMoleculeViewProtocol) { + self.molecule = molecule + super.init(frame: .zero) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func setupView() { + super.setupView() + if let molecule = molecule, molecule.superview == nil { + addSubview(molecule) + molecule.topAnchor.constraint(equalTo: topAnchor).isActive = true + molecule.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor).isActive = true + layoutMarginsGuide.rightAnchor.constraint(equalTo: molecule.rightAnchor).isActive = true + bottomAnchor.constraint(equalTo: molecule.bottomAnchor).isActive = true + } + } + + open override func updateView(_ size: CGFloat) { + if let molecule = molecule as? MVMCoreViewProtocol { + molecule.updateView(size) + } + MFStyler.setDefaultMarginsFor(self, size: size) + } + + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + guard let json = json, let molecule = molecule else { + return + } + molecule.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + if let backgroundColor = molecule.backgroundColor { + self.backgroundColor = backgroundColor + } + } + + open override func setAsMolecule() { + molecule?.setAsMolecule?() + } +} diff --git a/MVMCoreUI/Atoms/Views/MFLabel.m b/MVMCoreUI/Atoms/Views/MFLabel.m index 5fb19037..4fba3083 100644 --- a/MVMCoreUI/Atoms/Views/MFLabel.m +++ b/MVMCoreUI/Atoms/Views/MFLabel.m @@ -237,6 +237,10 @@ self.originalAttributedString = self.attributedText; } +- (BOOL)needsToBeConstrained { + return YES; +} + - (void)styleH1:(BOOL)scale { [MFStyler styleLabelH1:self genericScaling:NO]; [self setScale:scale]; diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index 780b65aa..43ea5f8c 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -131,6 +131,7 @@ CGFloat padding = [MFStyler defaultHorizontalPaddingForSize:size]; [self setLeftPinConstant:padding]; [self setRightPinConstant:padding]; + [MFStyler setDefaultMarginsForView:self size:size]; }]; } } @@ -138,6 +139,7 @@ #pragma mark - MVMCoreUIMoleculeViewProtocol - (void)setAsMolecule { + self.updateViewHorizontalDefaults = YES; } @end diff --git a/MVMCoreUI/BaseControllers/MFViewController.m b/MVMCoreUI/BaseControllers/MFViewController.m index 0c3b3f3d..1bd5e866 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.m +++ b/MVMCoreUI/BaseControllers/MFViewController.m @@ -521,6 +521,10 @@ } completion:completion]; } +- (BOOL)viewRespectsSystemMinimumLayoutMargins { + return NO; +} + #pragma mark - UITextField Functions // To Remove TextFields Bug: Keyboard is not dismissing after reaching textfield max length limit diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift new file mode 100644 index 00000000..a44fc4ee --- /dev/null +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -0,0 +1,255 @@ +// +// ThreeLayerTableViewController.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/18/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit +import MVMAnimationFramework + +open class ThreeLayerTableViewController: MFProgrammaticTableViewController { + // The three main views + private var topView: UIView? + private var bottomView: UIView? + private var headerView: UIView? + private var footerView: UIView? + private var safeAreaView: UIView? + var useMargins: Bool = true + var bottomViewOutsideOfScrollArea: Bool = false + private var topViewBottomConstraint: NSLayoutConstraint? + private var bottomViewTopConstraint: NSLayoutConstraint? + + //MARK:-MVMCoreViewProtocol + open override func updateViews() { + super.updateViews() + let width = view.bounds.width + MFStyler.setDefaultMarginsFor(contentView, size: width) + if let topView = topView as? MVMCoreViewProtocol { + topView.updateView(width) + showHeader() + } + if let bottomView = bottomView as? MVMCoreViewProtocol { + bottomView.updateView(width) + showFooter() + } + self.tableView?.reloadData() + } + + //MARK:-MFViewController + open override func newDataBuildScreen() { + super.newDataBuildScreen() + createViewForTableHeader() + createViewForTableFooter() + tableView?.reloadData() + } + + override open func viewDidLoad() { + super.viewDidLoad() + setToHaveNoSectionHeadersFooters() + // Do any additional setup after loading the view. + } + + //MARK:-Spacing + // If both are subclassed to return a value, then the buttons will not be pinned towards the bottom because neither spacing would try to fill the screen. + /// Space between the top view and the table sections, nil to fill. 0 default + open func spaceBelowTopView() -> CGFloat? { + return 0 + } + + /// Space between the bottom view and the table sections, nil to fill. nil default + open func spaceAboveBottomView() -> CGFloat? { + return nil + } + + /// can override to return a minimum fill space. + open func minimumFillSpace() -> CGFloat { + return 0 + } + + open override func updateViewConstraints() { + super.updateViewConstraints() + guard let tableView = tableView else { + return + } + + let minimumSpace: CGFloat = minimumFillSpace() + var currentSpace: CGFloat = 0 + var totalMinimumSpace: CGFloat = 0 + + var fillTop = false + if spaceBelowTopView() == nil, self.tableView?.tableHeaderView != nil { + fillTop = true + currentSpace += topViewBottomConstraint?.constant ?? 0 + totalMinimumSpace += minimumSpace + } + + var fillBottom = false + if spaceAboveBottomView() == nil, !bottomViewOutsideOfScrollArea, self.tableView?.tableFooterView != nil { + fillBottom = true + currentSpace += bottomViewTopConstraint?.constant ?? 0 + totalMinimumSpace += minimumSpace + } + + guard fillTop || fillBottom else { + return + } + let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace) + + // If the bottom view is outside of the scroll, then only the top view constraint is being used, so we have to double it to account for the bottom constraint not being there when we compare to the new value. + var currentSpaceForCompare: CGFloat = currentSpace + if fillTop && bottomViewOutsideOfScrollArea { + currentSpaceForCompare = currentSpace * 2; + } + + if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpaceForCompare, 0.1) { + if fillTop && fillBottom { + // space both + let half = newSpace / 2 + topViewBottomConstraint?.constant = half + bottomViewTopConstraint?.constant = half + showHeader() + showFooter() + } else if fillTop { + // Only top is spaced (half the size if the bottom view is out of the scroll because it needs to be sized as if there are two spacers but there is only one. + if bottomViewOutsideOfScrollArea { + topViewBottomConstraint?.constant = newSpace / 2 + } else { + topViewBottomConstraint?.constant = newSpace + } + showHeader() + } else if fillBottom { + // Only bottom is spaced. + bottomViewTopConstraint?.constant = newSpace + showFooter() + } + } + } + + //MARK:-Header Footer + /// Gets the top view and adds it to a spacing view, headerView, and then calls showHeader. + open func createViewForTableHeader() { + let topView = viewForTop() + self.topView = topView + + let headerView = MVMCoreUICommonViewsUtility.commonView() + headerView.addSubview(topView) + topView.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true + topView.leftAnchor.constraint(equalTo: headerView.leftAnchor).isActive = true + headerView.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true + topViewBottomConstraint = headerView.bottomAnchor.constraint(equalTo: topView.bottomAnchor, constant: spaceBelowTopView() ?? 0) + topViewBottomConstraint?.isActive = true + self.headerView = headerView + showHeader() + } + + /// Gets the bottom view and adds it to a spacing view, footerView, and then calls showFooter. + open func createViewForTableFooter() { + let bottomView = viewForBottom() + self.bottomView = bottomView + + let footerView = MVMCoreUICommonViewsUtility.commonView() + footerView.addSubview(bottomView) + bottomViewTopConstraint = bottomView.topAnchor.constraint(equalTo: footerView.topAnchor, constant: spaceAboveBottomView() ?? 0) + bottomViewTopConstraint?.isActive = true + bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true + footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true + footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true + self.footerView = footerView + showFooter() + } + + /// Takes the current headerView and adds it to the tableHeaderView + func showHeader() { + headerView?.removeFromSuperview() + tableView?.tableHeaderView = nil + guard let headerView = headerView else { + return + } + + // This extra view is needed because of the wonkiness of apple's table header. Things breaks if using autolayout. + MVMCoreUIUtility.sizeView(toFit: headerView) + let tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: MVMCoreUIUtility.getWidth(), height: headerView.frame.height)) + tableHeaderView.addSubview(headerView) + NSLayoutConstraint.constraintPinSubview(toSuperview: headerView) + tableView?.tableHeaderView = tableHeaderView + } + + /// Takes the current footerView and adds it to the tableFooterView + func showFooter() { + footerView?.removeFromSuperview() + safeAreaView?.removeFromSuperview() + guard let footerView = footerView, let tableView = tableView else { + return + } + + if bottomViewOutsideOfScrollArea { + // put bottom view outside of scrolling area. + bottomConstraint?.isActive = false + view.addSubview(footerView) + footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true + footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true + view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true + if #available(iOS 11.0, *) { + view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true + safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view) + safeAreaView?.backgroundColor = bottomView?.backgroundColor + } else { + view.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true + } + } else { + bottomConstraint?.isActive = true + var y: CGFloat? + if let tableFooterView = tableView.tableFooterView { + // if footer already exists, use the same y location to avoid strange moving animation + y = tableFooterView.frame.minY + } + + // This extra view is needed because of the wonkiness of apple's table footer. Things breaks if using autolayout. + MVMCoreUIUtility.sizeView(toFit: footerView) + let tableFooterView = UIView(frame: CGRect(x: 0, y: y ?? 0, width: MVMCoreUIUtility.getWidth(), height: footerView.frame.height)) + tableFooterView.addSubview(footerView) + NSLayoutConstraint.constraintPinSubview(toSuperview: footerView) + tableView.tableFooterView = tableFooterView + } + } + + //MARK:-Functions to subclass + /// Subclass for a top view. + open func viewForTop() -> UIView { + let view = MVMCoreUICommonViewsUtility.commonView() + view.heightAnchor.constraint(equalToConstant: 0).isActive = true + return view + } + + /// Subclass for a bottom view. + open func viewForBottom() -> UIView { + let view = MVMCoreUICommonViewsUtility.commonView() + view.heightAnchor.constraint(equalToConstant: 0).isActive = true + return view + } + + //MARK:-Scrollview + open override func scrollViewDidScroll(_ scrollView: UIScrollView) { + // To stop handscroll animation if animating after scroll + stopHandScrollAnimation(true) + } + + deinit { + tableView?.delegate = nil + } + + //MARK:-Animation + open override func setupIntroAnimations() { + if let topView = topView, topView.subviews.count > 0 { + introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: topView)) + } + if let tableView = tableView { + introAnimationManager?.addAnimation(animation: MVMAnimations.animateTableViewFadeInCells(tableView: tableView)) + } + if let bottomView = bottomView, bottomView.subviews.count > 0 { + introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: bottomView)) + } + } +} diff --git a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift index 2573388c..299cdf9b 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift @@ -16,7 +16,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController { var topView: UIView? var middleView: UIView? var bottomView: UIView? - var useMargins: Bool = true + var useMargins: Bool = false // The bottom view can be put outside of the scrolling area. var bottomViewOutsideOfScroll = false diff --git a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h index ac92e0a1..cce08641 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h +++ b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h @@ -19,6 +19,9 @@ // Called after init to provide an early setter for any molecule specific logic - (void)setAsMolecule; +// Notifies the creator that the view needs to be constrained in a view. +- (BOOL)needsToBeConstrained; + @end diff --git a/MVMCoreUI/Molecules/MoleculeStackView.swift b/MVMCoreUI/Molecules/MoleculeStackView.swift index 57b0108c..a43a6a94 100644 --- a/MVMCoreUI/Molecules/MoleculeStackView.swift +++ b/MVMCoreUI/Molecules/MoleculeStackView.swift @@ -70,12 +70,9 @@ public class MoleculeStackView: MFView { if let spacingBlock = spacingBlock { MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray, useMargins: useMargins, withSpacingBlock: spacingBlock) } else { + let separation = json?.optionalCGFloatForKey("separation") ?? PaddingDefault MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray, useMargins: useMargins) { (object) -> UIEdgeInsets in - if object as AnyObject? === moleculesArray.first { - return UIEdgeInsets.zero - } else { - return UIEdgeInsets.init(top: PaddingTwo, left: 0, bottom: 0, right: 0) - } + return UIEdgeInsets.init(top: separation, left: 0, bottom: 0, right: 0) } } } diff --git a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift new file mode 100644 index 00000000..21b05a22 --- /dev/null +++ b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift @@ -0,0 +1,34 @@ +// +// MoleculeTableViewCell.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/18/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers open class MoleculeTableViewCell: UITableViewCell, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol { + var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + + public func updateView(_ size: CGFloat) { + if let molecule = molecule as? MVMCoreViewProtocol { + molecule.updateView(size) + } + } + + public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { + guard let json = json else { + return + } + if molecule == nil { + if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(json, delegateObject: delegateObject) { + addSubview(moleculeView) + NSLayoutConstraint.constraintPinSubview(toSuperview: moleculeView) + molecule = moleculeView + } + } else { + molecule?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + } + } +} diff --git a/MVMCoreUI/Molecules/StandardFooterView.swift b/MVMCoreUI/Molecules/StandardFooterView.swift index a170d023..06d39c47 100644 --- a/MVMCoreUI/Molecules/StandardFooterView.swift +++ b/MVMCoreUI/Molecules/StandardFooterView.swift @@ -46,16 +46,16 @@ public class StandardFooterView: ViewConstrainingView { spaceBetweenButtons = textButton.topAnchor.constraint(equalTo: twoButtonView.bottomAnchor, constant: PaddingTwo) spaceBetweenButtons?.isActive = true - leftConstraintTwoButton = twoButtonView.leftAnchor.constraint(equalTo: leftAnchor) + leftConstraintTwoButton = twoButtonView.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor) leftConstraintTwoButton?.isActive = true - rightConstraintTwoButton = rightAnchor.constraint(equalTo: twoButtonView.rightAnchor) + rightConstraintTwoButton = layoutMarginsGuide.rightAnchor.constraint(equalTo: twoButtonView.rightAnchor) rightConstraintTwoButton?.isActive = true - leftConstraintTextButton = textButton.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor) + leftConstraintTextButton = textButton.leftAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.leftAnchor) leftConstraintTextButton?.isActive = true - rightConstraintTextButton = rightAnchor.constraint(greaterThanOrEqualTo: textButton.rightAnchor) + rightConstraintTextButton = layoutMarginsGuide.rightAnchor.constraint(greaterThanOrEqualTo: textButton.rightAnchor) rightConstraintTextButton?.isActive = true centerAlignTextButton = textButton.centerXAnchor.constraint(equalTo: centerXAnchor) @@ -93,16 +93,6 @@ public class StandardFooterView: ViewConstrainingView { layoutIfNeeded() } - public override func setLeftPinConstant(_ constant: CGFloat) { - leftConstraintTwoButton?.constant = constant - leftConstraintTextButton?.constant = constant - } - - public override func setRightPinConstant(_ constant: CGFloat) { - rightConstraintTwoButton?.constant = constant - rightConstraintTextButton?.constant = constant - } - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) if let colorString = json?.optionalStringForKey(KeyBackgroundColor) { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index c1d7a36d..9407e5c7 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -22,7 +22,7 @@ static NSMutableDictionary *mapping; dispatch_once(&onceToken, ^{ mapping = [@{ - @"label": LabelView.class, + @"label": MFLabel.class, @"separator": SeparatorView.class, @"button": ButtonView.class, @"textButton": MFTextButton.class, @@ -47,6 +47,9 @@ Class class = [self.moleculeMapping objectForKey:name]; if (class) { UIView *view = [[class alloc] init]; + if ([view respondsToSelector:@selector(needsToBeConstrained)] && [view needsToBeConstrained]) { + view = [[ConstrainingMoleculeView alloc] initWithMolecule:view]; + } if ([view respondsToSelector:@selector(setAsMolecule)]) { [view setAsMolecule]; } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m index 841a7e60..4e33da75 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m @@ -21,7 +21,8 @@ viewControllerMapping = [@{ @"textFieldListForm" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[TextFieldListFormViewController class]], @"moleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackTemplate class]], - @"centerMoleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackCenteredTemplate class]] + @"centerMoleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackCenteredTemplate class]], + @"moleculeList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeListTemplate class]] } mutableCopy]; }); return viewControllerMapping; diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift new file mode 100644 index 00000000..824b7ea2 --- /dev/null +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -0,0 +1,57 @@ +// +// MoleculeListTemplate.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/18/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +public class MoleculeListTemplate: ThreeLayerTableViewController { + + public override func registerWithTable() { + super.registerWithTable() + guard let molecules = loadObject?.pageJSON?.arrayForKey("molecules") else { + return + } + for case let molecule as Dictionary in molecules { + if let moleculeName = molecule.optionalStringForKey("moleculeName") { + tableView?.register(MoleculeTableViewCell.self, forCellReuseIdentifier: moleculeName) + } + } + } + + public override func viewForTop() -> UIView { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + return super.viewForTop() + } + return molecule + } + + override public func viewForBottom() -> UIView { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + return viewForBottom() + } + return molecule + } + + public override func newDataBuildScreen() { + super.newDataBuildScreen() + registerWithTable() + } + + public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return loadObject?.pageJSON?.arrayForKey("molecules").count ?? 0 + } + + public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let molecule = loadObject?.pageJSON?.optionalDictionaryWithChainOfKeysOrIndexes(["molecules",indexPath.row]), + let moleculeName = molecule.optionalStringForKey("moleculeName"), + let cell = tableView.dequeueReusableCell(withIdentifier: moleculeName) as? MoleculeTableViewCell else { + return UITableViewCell() + } + cell.setWithJSON(molecule, delegateObject: delegateObject(), additionalData: nil) + return cell + } +} diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index dfcef277..8b3d652d 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -12,7 +12,7 @@ public class MoleculeStackTemplate: ThreeLayerViewController { public override func spaceBetweenTopAndMiddle() -> CGFloat? { - return PaddingTwo + return 0 } public override func viewForTop() -> UIView? { From 90d22908430318af21abfcd624fe6058368ca94c Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 24 Apr 2019 14:30:16 -0400 Subject: [PATCH 2/7] remove extra view I created --- MVMCoreUI.xcodeproj/project.pbxproj | 4 -- .../Views/ConstrainingMoleculeView.swift | 55 ---------------- MVMCoreUI/Atoms/Views/ViewConstrainingView.h | 6 ++ MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 64 +++++++++++++------ .../MVMCoreUIMoleculeMappingObject.m | 2 +- 5 files changed, 50 insertions(+), 81 deletions(-) delete mode 100644 MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 6cb9dc9e..b75c10db 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -147,7 +147,6 @@ D29DF32521ED0DA2003B2FB9 /* TextButtonView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF32321ED0DA2003B2FB9 /* TextButtonView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF32C21EE8736003B2FB9 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D29DF32821EE8736003B2FB9 /* Localizable.strings */; }; D29DF32E21EE8C3D003B2FB9 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D29DF32D21EE8C3D003B2FB9 /* Media.xcassets */; }; - D2A421BF226A14F100A05A88 /* ConstrainingMoleculeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A421BE226A14F100A05A88 /* ConstrainingMoleculeView.swift */; }; D2A514582211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */; }; D2A5145D2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -312,7 +311,6 @@ D29DF32A21EE8736003B2FB9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; D29DF32B21EE8736003B2FB9 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Localizable.strings"; sourceTree = ""; }; D29DF32D21EE8C3D003B2FB9 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; - D2A421BE226A14F100A05A88 /* ConstrainingMoleculeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstrainingMoleculeView.swift; sourceTree = ""; }; D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeMappingObject.h; sourceTree = ""; }; D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIMoleculeMappingObject.m; sourceTree = ""; }; D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeViewProtocol.h; sourceTree = ""; }; @@ -579,7 +577,6 @@ D29DF28621E7AC2B003B2FB9 /* MFLabel.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, D29DF31F21ED0CBA003B2FB9 /* LabelView.m */, - D2A421BE226A14F100A05A88 /* ConstrainingMoleculeView.swift */, D29DF28721E7AC2B003B2FB9 /* ViewConstrainingView.h */, D29DF28821E7AC2B003B2FB9 /* ViewConstrainingView.m */, D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */, @@ -880,7 +877,6 @@ D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, - D2A421BF226A14F100A05A88 /* ConstrainingMoleculeView.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift b/MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift deleted file mode 100644 index 22e8e09d..00000000 --- a/MVMCoreUI/Atoms/Views/ConstrainingMoleculeView.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ConstrainingMoleculeView.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 4/19/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - -@objcMembers open class ConstrainingMoleculeView: MFView { - var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? - - public init(withMolecule molecule: UIView & MVMCoreUIMoleculeViewProtocol) { - self.molecule = molecule - super.init(frame: .zero) - } - - required public init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - open override func setupView() { - super.setupView() - if let molecule = molecule, molecule.superview == nil { - addSubview(molecule) - molecule.topAnchor.constraint(equalTo: topAnchor).isActive = true - molecule.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor).isActive = true - layoutMarginsGuide.rightAnchor.constraint(equalTo: molecule.rightAnchor).isActive = true - bottomAnchor.constraint(equalTo: molecule.bottomAnchor).isActive = true - } - } - - open override func updateView(_ size: CGFloat) { - if let molecule = molecule as? MVMCoreViewProtocol { - molecule.updateView(size) - } - MFStyler.setDefaultMarginsFor(self, size: size) - } - - open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - guard let json = json, let molecule = molecule else { - return - } - molecule.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - if let backgroundColor = molecule.backgroundColor { - self.backgroundColor = backgroundColor - } - } - - open override func setAsMolecule() { - molecule?.setAsMolecule?() - } -} diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.h b/MVMCoreUI/Atoms/Views/ViewConstrainingView.h index bb4740a7..377c7821 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.h +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.h @@ -26,6 +26,9 @@ // Returns a view with the provided view as a subview, pinned. + (nonnull ViewConstrainingView *)viewConstrainingView:(nonnull UIView *)view; +// Can be initialized with a molecule to constrain +- (nullable instancetype)initWithMolecule:(nonnull UIView *)molecule; + // Use these to sets the constants, because subclasses may align differently. - (void)setPinConstantsWithInsets:(UIEdgeInsets)insets; - (void)setTopPinConstant:(CGFloat)top left:(CGFloat)left bottom:(CGFloat)bottom right:(CGFloat)right; @@ -41,6 +44,9 @@ // Pins all edges to its super. 0 constant - (void)pinToSuperView; +// Add a view to be constrained in this view. +- (void)addConstrainedView:(nonnull UIView *)view; + // Resets all the constraints to default. - (void)resetConstraints; diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index 43ea5f8c..1c96040b 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -13,10 +13,18 @@ @interface ViewConstrainingView () @property (weak, nullable, nonatomic) UIView *constrainedView; +@property (strong, nullable, nonatomic) UIView *molecule; @end @implementation ViewConstrainingView +- (nullable instancetype)initWithMolecule:(nonnull UIView *)molecule { + if (self = [super init]) { + self.molecule = molecule; + } + return self; +} + + (nonnull ViewConstrainingView *)emptyView { ViewConstrainingView *view = [[ViewConstrainingView alloc] initWithFrame:CGRectZero]; view.translatesAutoresizingMaskIntoConstraints = NO; @@ -28,27 +36,7 @@ ViewConstrainingView *constrainingView = [[ViewConstrainingView alloc] initWithFrame:CGRectZero]; constrainingView.translatesAutoresizingMaskIntoConstraints = NO; constrainingView.backgroundColor = [UIColor clearColor]; - - view.translatesAutoresizingMaskIntoConstraints = NO; - [constrainingView addSubview:view]; - constrainingView.constrainedView = view; - - NSLayoutConstraint *leftPin = [view.leftAnchor constraintEqualToAnchor:constrainingView.leftAnchor]; - constrainingView.leftPin = leftPin; - leftPin.active = YES; - - NSLayoutConstraint *topPin = [view.topAnchor constraintEqualToAnchor:constrainingView.topAnchor]; - constrainingView.topPin = topPin; - topPin.active = YES; - - NSLayoutConstraint *bottomPin = [constrainingView.bottomAnchor constraintEqualToAnchor:view.bottomAnchor]; - constrainingView.bottomPin = bottomPin; - bottomPin.active = YES; - - NSLayoutConstraint *rightPin = [constrainingView.rightAnchor constraintEqualToAnchor:view.rightAnchor]; - constrainingView.rightPin = rightPin; - rightPin.active = YES; - + [constrainingView addConstrainedView:view]; return constrainingView; } @@ -115,10 +103,35 @@ self.backgroundColor = [UIColor clearColor]; } +- (void)addConstrainedView:(nonnull UIView *)view { + view.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:view]; + self.constrainedView = view; + + NSLayoutConstraint *leftPin = [view.leftAnchor constraintEqualToAnchor:self.leftAnchor]; + self.leftPin = leftPin; + leftPin.active = YES; + + NSLayoutConstraint *topPin = [view.topAnchor constraintEqualToAnchor:self.topAnchor]; + self.topPin = topPin; + topPin.active = YES; + + NSLayoutConstraint *bottomPin = [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor]; + self.bottomPin = bottomPin; + bottomPin.active = YES; + + NSLayoutConstraint *rightPin = [self.rightAnchor constraintEqualToAnchor:view.rightAnchor]; + self.rightPin = rightPin; + rightPin.active = YES; +} + - (void)setupView { [super setupView]; self.translatesAutoresizingMaskIntoConstraints = NO; self.backgroundColor = [UIColor clearColor]; + if (!self.molecule.superview) { + [self addConstrainedView:self.molecule]; + } } - (void)updateView:(CGFloat)size { @@ -140,6 +153,15 @@ - (void)setAsMolecule { self.updateViewHorizontalDefaults = YES; + [self.molecule setAsMolecule]; +} + +- (void)setWithJSON:(NSDictionary *)json delegateObject:(DelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { + [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; + if (self.molecule) { + [self.molecule setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; + self.backgroundColor = self.molecule.backgroundColor; + } } @end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 9407e5c7..b757c78b 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -48,7 +48,7 @@ if (class) { UIView *view = [[class alloc] init]; if ([view respondsToSelector:@selector(needsToBeConstrained)] && [view needsToBeConstrained]) { - view = [[ConstrainingMoleculeView alloc] initWithMolecule:view]; + view = [[ViewConstrainingView alloc] initWithMolecule:view]; } if ([view respondsToSelector:@selector(setAsMolecule)]) { [view setAsMolecule]; From 1573e57f2caba2751f4eb192ba9bbbd1371dede8 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 24 Apr 2019 15:38:59 -0400 Subject: [PATCH 3/7] constraining --- MVMCoreUI/Atoms/Views/Label.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 8ca484e3..2d53adb8 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -301,6 +301,10 @@ import MVMCore originalAttributedString = attributedText } + + public func needsToBeConstrained() -> Bool { + return true; + } } extension Label { From 8a349ae16f6940a17ecdb9ca5291d038360fd6c2 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 24 Apr 2019 16:14:22 -0400 Subject: [PATCH 4/7] constraining for stack --- MVMCoreUI/Atoms/Buttons/CaretButton.swift | 4 ++++ MVMCoreUI/Atoms/Buttons/MFTextButton.m | 4 ++++ MVMCoreUI/Atoms/Views/CaretView.swift | 5 ++++- MVMCoreUI/Atoms/Views/Label.swift | 2 -- MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 12 +++++++----- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/CaretButton.swift b/MVMCoreUI/Atoms/Buttons/CaretButton.swift index 6aa4a754..47697980 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretButton.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretButton.swift @@ -131,4 +131,8 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol { disabledColor = UIColor.mfGet(forHex: disabledColorHex) } } + + public func needsToBeConstrained() -> Bool { + return true + } } diff --git a/MVMCoreUI/Atoms/Buttons/MFTextButton.m b/MVMCoreUI/Atoms/Buttons/MFTextButton.m index 0a72eee7..96a99a18 100644 --- a/MVMCoreUI/Atoms/Buttons/MFTextButton.m +++ b/MVMCoreUI/Atoms/Buttons/MFTextButton.m @@ -141,4 +141,8 @@ } } +- (BOOL)needsToBeConstrained { + return YES; +} + @end diff --git a/MVMCoreUI/Atoms/Views/CaretView.swift b/MVMCoreUI/Atoms/Views/CaretView.swift index eb3a4c4b..fff6181e 100644 --- a/MVMCoreUI/Atoms/Views/CaretView.swift +++ b/MVMCoreUI/Atoms/Views/CaretView.swift @@ -91,7 +91,6 @@ open class CaretView: MFView { // Default values for view. @objc open override func setAsMolecule() { - defaultState() } @@ -120,4 +119,8 @@ open class CaretView: MFView { lineWidth = lineWidthValue } } + + open override func needsToBeConstrained() -> Bool { + return true + } } diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 2d53adb8..0cd77ee0 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -268,7 +268,6 @@ import MVMCore } }) attributedText = attributedString - } else if !MVMCoreGetterUtility.fequal(a: Float(standardFontSize), b: 0.0), let sizeObject: MFSizeObject = self.sizeObject ?? MFStyler.sizeObjectGeneric(forCurrentDevice: standardFontSize) { self.font = self.font.withSize(sizeObject.getValueBased(onSize: size)) } @@ -298,7 +297,6 @@ import MVMCore @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData) - originalAttributedString = attributedText } diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index 1c96040b..d14163a6 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -13,13 +13,16 @@ @interface ViewConstrainingView () @property (weak, nullable, nonatomic) UIView *constrainedView; -@property (strong, nullable, nonatomic) UIView *molecule; +@property (weak, nullable, nonatomic) UIView *molecule; @end @implementation ViewConstrainingView - (nullable instancetype)initWithMolecule:(nonnull UIView *)molecule { if (self = [super init]) { + if (!molecule.superview) { + [self addConstrainedView:molecule]; + } self.molecule = molecule; } return self; @@ -129,9 +132,6 @@ [super setupView]; self.translatesAutoresizingMaskIntoConstraints = NO; self.backgroundColor = [UIColor clearColor]; - if (!self.molecule.superview) { - [self addConstrainedView:self.molecule]; - } } - (void)updateView:(CGFloat)size { @@ -153,7 +153,9 @@ - (void)setAsMolecule { self.updateViewHorizontalDefaults = YES; - [self.molecule setAsMolecule]; + if ([self.molecule respondsToSelector:@selector(setAsMolecule)]) { + [self.molecule setAsMolecule]; + } } - (void)setWithJSON:(NSDictionary *)json delegateObject:(DelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { From d3361fedebba3db1008dd76e60a1611cbb7fc720 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 24 Apr 2019 16:21:16 -0400 Subject: [PATCH 5/7] molecule list fix --- MVMCoreUI/Templates/MoleculeListTemplate.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 824b7ea2..f1226056 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -52,6 +52,7 @@ public class MoleculeListTemplate: ThreeLayerTableViewController { return UITableViewCell() } cell.setWithJSON(molecule, delegateObject: delegateObject(), additionalData: nil) + cell.updateView(tableView.bounds.width) return cell } } From 302a2f887fadcf336e29ef0368209a0fed625583 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 29 Apr 2019 13:36:36 -0400 Subject: [PATCH 6/7] thickness. molecule alignment --- MVMCoreUI/Atoms/Buttons/CaretButton.swift | 4 ++ MVMCoreUI/Atoms/Buttons/MFTextButton.m | 4 ++ MVMCoreUI/Atoms/Views/CaretView.swift | 25 +++++-- MVMCoreUI/Atoms/Views/MFView.m | 2 +- MVMCoreUI/Atoms/Views/ViewConstrainingView.h | 2 +- MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 69 +++++++++---------- .../NSLayoutConstraint+MFConvenience.h | 5 ++ .../NSLayoutConstraint+MFConvenience.m | 24 +++++-- MVMCoreUI/Molecules/MoleculeStackView.swift | 3 +- .../Molecules/MoleculeTableViewCell.swift | 52 +++++++++++++- .../MVMCoreUIMoleculeViewProtocol.h | 6 ++ .../MVMCoreUIMoleculeMappingObject.h | 3 + .../MVMCoreUIMoleculeMappingObject.m | 32 ++++++--- .../Templates/MoleculeListTemplate.swift | 12 +++- .../MoleculeStackCenteredTemplate.swift | 4 +- .../Templates/MoleculeStackTemplate.swift | 4 +- 16 files changed, 179 insertions(+), 72 deletions(-) rename MVMCoreUI/Molecules/{ => Protocols}/MVMCoreUIMoleculeViewProtocol.h (79%) diff --git a/MVMCoreUI/Atoms/Buttons/CaretButton.swift b/MVMCoreUI/Atoms/Buttons/CaretButton.swift index 47697980..c8bf3fdf 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretButton.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretButton.swift @@ -135,4 +135,8 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol { public func needsToBeConstrained() -> Bool { return true } + + open func moleculeAlignment() -> UIStackView.Alignment { + return UIStackView.Alignment.leading; + } } diff --git a/MVMCoreUI/Atoms/Buttons/MFTextButton.m b/MVMCoreUI/Atoms/Buttons/MFTextButton.m index 96a99a18..d78df33d 100644 --- a/MVMCoreUI/Atoms/Buttons/MFTextButton.m +++ b/MVMCoreUI/Atoms/Buttons/MFTextButton.m @@ -145,4 +145,8 @@ return YES; } +- (UIStackViewAlignment)moleculeAlignment { + return UIStackViewAlignmentLeading; +} + @end diff --git a/MVMCoreUI/Atoms/Views/CaretView.swift b/MVMCoreUI/Atoms/Views/CaretView.swift index fff6181e..2be52f11 100644 --- a/MVMCoreUI/Atoms/Views/CaretView.swift +++ b/MVMCoreUI/Atoms/Views/CaretView.swift @@ -13,9 +13,15 @@ open class CaretView: MFView { // MARK: - Properties //------------------------------------------------------ + // Objc can't use float enum. + @objc public static let thin: CGFloat = 6.0 + @objc public static let standard: CGFloat = 2.6 + @objc public static let thick: CGFloat = 1.5 + private(set) var strokeColor: UIColor? private var lineWidth: CGFloat? - + private var lineThickness: CGFloat? + //------------------------------------------------------ // MARK: - Initialization //------------------------------------------------------ @@ -32,14 +38,19 @@ open class CaretView: MFView { super.init(coder: aDecoder) } + /// Can init with a specific line width. @objc public init(lineWidth: CGFloat) { super.init(frame: CGRect()) - self.lineWidth = lineWidth } + /// Can init with a specific line thickness, scales based on width and height. + @objc public init(lineThickness: CGFloat) { + super.init(frame: CGRect()) + self.lineThickness = lineThickness + } + @objc override open func setupView() { - defaultState() } @@ -48,7 +59,6 @@ open class CaretView: MFView { //------------------------------------------------------ private func defaultState() { - isOpaque = false isHidden = false backgroundColor = .clear @@ -64,7 +74,7 @@ open class CaretView: MFView { let context = UIGraphicsGetCurrentContext() context?.clear(rect) - let lineWidthToDraw: CGFloat = lineWidth ?? frame.size.width / 2.6 + let lineWidthToDraw: CGFloat = lineWidth ?? frame.size.width / (lineThickness ?? 2.6) let path = UIBezierPath() path.move(to: CGPoint(x: lineWidthToDraw / 2.0, y: 0.0)) @@ -80,7 +90,6 @@ open class CaretView: MFView { } @objc public func setLineColor(_ color: UIColor?) { - strokeColor = color setNeedsDisplay() } @@ -123,4 +132,8 @@ open class CaretView: MFView { open override func needsToBeConstrained() -> Bool { return true } + + open override func moleculeAlignment() -> UIStackView.Alignment { + return UIStackView.Alignment.leading; + } } diff --git a/MVMCoreUI/Atoms/Views/MFView.m b/MVMCoreUI/Atoms/Views/MFView.m index 4bd73485..b22a5674 100644 --- a/MVMCoreUI/Atoms/Views/MFView.m +++ b/MVMCoreUI/Atoms/Views/MFView.m @@ -36,7 +36,7 @@ } - (void)setupView { - + self.preservesSuperviewLayoutMargins = YES; } - (void)updateView:(CGFloat)size { diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.h b/MVMCoreUI/Atoms/Views/ViewConstrainingView.h index 377c7821..528a79e6 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.h +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.h @@ -27,7 +27,7 @@ + (nonnull ViewConstrainingView *)viewConstrainingView:(nonnull UIView *)view; // Can be initialized with a molecule to constrain -- (nullable instancetype)initWithMolecule:(nonnull UIView *)molecule; +- (nullable instancetype)initWithMolecule:(nonnull UIView *)molecule alignment:(UIStackViewAlignment)alignment; // Use these to sets the constants, because subclasses may align differently. - (void)setPinConstantsWithInsets:(UIEdgeInsets)insets; diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index d14163a6..861db8cd 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -9,6 +9,7 @@ #import "ViewConstrainingView.h" @import MVMCore.MVMCoreConstants; @import MVMCore.MVMCoreDispatchUtility; +#import "NSLayoutConstraint+MFConvenience.h" #import "MFStyler.h" @interface ViewConstrainingView () @@ -18,10 +19,11 @@ @implementation ViewConstrainingView -- (nullable instancetype)initWithMolecule:(nonnull UIView *)molecule { +- (nullable instancetype)initWithMolecule:(nonnull UIView *)molecule alignment:(UIStackViewAlignment)alignment { if (self = [super init]) { if (!molecule.superview) { - [self addConstrainedView:molecule]; + [self addConstrainedView:molecule alignment:alignment]; + [self setAsMolecule]; } self.molecule = molecule; } @@ -44,23 +46,11 @@ } - (void)pinToSuperView { - - // Align left and right constants. - NSLayoutConstraint *leftPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - self.leftPin = leftPin; - leftPin.active = YES; - - NSLayoutConstraint *topPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; - self.topPin = topPin; - topPin.active = YES; - - NSLayoutConstraint *bottomPin = [NSLayoutConstraint constraintWithItem:self.superview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; - self.bottomPin = bottomPin; - bottomPin.active = YES; - - NSLayoutConstraint *rightPin = [NSLayoutConstraint constraintWithItem:self.superview attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - self.rightPin = rightPin; - rightPin.active = YES; + NSDictionary *dictionary = [NSLayoutConstraint constraintPinSubviewToSuperview:self]; + self.leftPin = dictionary[ConstraintLeading]; + self.topPin = dictionary[ConstraintTop]; + self.bottomPin = dictionary[ConstraintBot]; + self.rightPin = dictionary[ConstraintTrailing]; } - (void)setPinConstantsWithInsets:(UIEdgeInsets)insets { @@ -106,26 +96,32 @@ self.backgroundColor = [UIColor clearColor]; } -- (void)addConstrainedView:(nonnull UIView *)view { +- (void)addConstrainedView:(nonnull UIView *)view alignment:(UIStackViewAlignment)alignment { view.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:view]; self.constrainedView = view; - NSLayoutConstraint *leftPin = [view.leftAnchor constraintEqualToAnchor:self.leftAnchor]; - self.leftPin = leftPin; - leftPin.active = YES; - - NSLayoutConstraint *topPin = [view.topAnchor constraintEqualToAnchor:self.topAnchor]; - self.topPin = topPin; - topPin.active = YES; - - NSLayoutConstraint *bottomPin = [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor]; - self.bottomPin = bottomPin; - bottomPin.active = YES; - - NSLayoutConstraint *rightPin = [self.rightAnchor constraintEqualToAnchor:view.rightAnchor]; - self.rightPin = rightPin; - rightPin.active = YES; + NSLayoutRelation leftRelation; + if (alignment == UIStackViewAlignmentFill || alignment == UIStackViewAlignmentLeading || alignment == UIStackViewAlignmentFirstBaseline) { + leftRelation = NSLayoutRelationEqual; + } else { + leftRelation = NSLayoutRelationGreaterThanOrEqual; + } + NSLayoutRelation rightRelation; + if (alignment == UIStackViewAlignmentFill || alignment == UIStackViewAlignmentTrailing || alignment == UIStackViewAlignmentLastBaseline) { + rightRelation = NSLayoutRelationEqual; + } else { + rightRelation = NSLayoutRelationGreaterThanOrEqual; + } + NSDictionary *dictionary = [NSLayoutConstraint constraintPinSubview:view topRelation:NSLayoutRelationEqual bottomRelation:NSLayoutRelationEqual leftRelation:leftRelation rightRelation:rightRelation]; + self.leftPin = dictionary[ConstraintLeading]; + self.topPin = dictionary[ConstraintTop]; + self.bottomPin = dictionary[ConstraintBot]; + self.rightPin = dictionary[ConstraintTrailing]; +} + +- (void)addConstrainedView:(nonnull UIView *)view { + [self addConstrainedView:view alignment:UIStackViewAlignmentFill]; } - (void)setupView { @@ -153,9 +149,6 @@ - (void)setAsMolecule { self.updateViewHorizontalDefaults = YES; - if ([self.molecule respondsToSelector:@selector(setAsMolecule)]) { - [self.molecule setAsMolecule]; - } } - (void)setWithJSON:(NSDictionary *)json delegateObject:(DelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { diff --git a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h index c8e12cae..0d0d281c 100644 --- a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h +++ b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h @@ -31,8 +31,12 @@ extern NSString * _Nonnull const ConstraintWidth; + (nullable NSDictionary *)constraintPinSubview:(nullable UIView *)subview pinTop:(BOOL)pinTop pinBottom:(BOOL)pinBottom pinLeft:(BOOL)pinLeft pinRight:(BOOL)pinRight; ++ (nullable NSDictionary *)constraintPinSubview:(nullable UIView *)subview topRelation:(NSLayoutRelation)topRelation bottomRelation:(NSLayoutRelation)bottomRelation leftRelation:(NSLayoutRelation)leftRelation rightRelation:(NSLayoutRelation)rightRelation; + + (nullable NSDictionary *)constraintPinSubview:(nullable UIView *)subview pinTop:(BOOL)pinTop topConstant:(CGFloat)topConstant pinBottom:(BOOL)pinBottom bottomConstant:(CGFloat)bottomConstant pinLeft:(BOOL)pinLeft leftConstant:(CGFloat)leftConstant pinRight:(BOOL)pinRight rightConstant:(CGFloat)rightConstant; ++ (nullable NSDictionary *)constraintPinSubview:(nullable UIView *)subview pinTop:(BOOL)pinTop topConstant:(CGFloat)topConstant topRelation:(NSLayoutRelation)topRelation pinBottom:(BOOL)pinBottom bottomConstant:(CGFloat)bottomConstant bottomRelation:(NSLayoutRelation)bottomRelation pinLeft:(BOOL)pinLeft leftConstant:(CGFloat)leftConstant leftRelation:(NSLayoutRelation)leftRelation pinRight:(BOOL)pinRight rightConstant:(CGFloat)rightConstant rightRelation:(NSLayoutRelation)rightRelation; + // Pin subview with 1 side + (nullable NSDictionary *)constraintPinTopSubview:(nonnull UIView *)subview topConstant:(CGFloat)topConstant; + (nullable NSDictionary *)constraintPinBottomSubview:(nonnull UIView *)subview bottomConstant:(CGFloat)bottomConstant; @@ -57,6 +61,7 @@ extern NSString * _Nonnull const ConstraintWidth; #pragma mark - With Margins ++ (nonnull NSDictionary *)pinViewToSuperview:(nonnull UIView *)subview useMargins:(BOOL)useMargins; + (nonnull NSLayoutConstraint *)pinViewTopToSuperview:(nonnull UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant; + (nonnull NSLayoutConstraint *)pinViewLeftToSuperview:(nonnull UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant; + (nonnull NSLayoutConstraint *)pinViewRightToSuperview:(nonnull UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant; diff --git a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m index 39a315c1..72aad96f 100644 --- a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m +++ b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m @@ -44,29 +44,37 @@ NSString *const ConstraintWidth = @"width"; return [NSLayoutConstraint constraintPinSubview:subview pinTop:pinTop topConstant:0 pinBottom:pinBottom bottomConstant:0 pinLeft:pinLeft leftConstant:0 pinRight:pinRight rightConstant:0]; } ++ (nullable NSDictionary *)constraintPinSubview:(nullable UIView *)subview topRelation:(NSLayoutRelation)topRelation bottomRelation:(NSLayoutRelation)bottomRelation leftRelation:(NSLayoutRelation)leftRelation rightRelation:(NSLayoutRelation)rightRelation { + return [self constraintPinSubview:subview pinTop:YES topConstant:0 topRelation:topRelation pinBottom:YES bottomConstant:0 bottomRelation:bottomRelation pinLeft:YES leftConstant:0 leftRelation:leftRelation pinRight:YES rightConstant:0 rightRelation:rightRelation]; +} + + (NSDictionary *)constraintPinSubview:(UIView *)subview pinTop:(BOOL)pinTop topConstant:(CGFloat)topConstant pinBottom:(BOOL)pinBottom bottomConstant:(CGFloat)bottomConstant pinLeft:(BOOL)pinLeft leftConstant:(CGFloat)leftConstant pinRight:(BOOL)pinRight rightConstant:(CGFloat)rightConstant { + return [self constraintPinSubview:subview pinTop:pinTop topConstant:topConstant topRelation:NSLayoutRelationEqual pinBottom:pinBottom bottomConstant:bottomConstant bottomRelation:NSLayoutRelationEqual pinLeft:pinLeft leftConstant:leftConstant leftRelation:NSLayoutRelationEqual pinRight:pinRight rightConstant:rightConstant rightRelation:NSLayoutRelationEqual]; +} + ++ (NSDictionary *)constraintPinSubview:(UIView *)subview pinTop:(BOOL)pinTop topConstant:(CGFloat)topConstant topRelation:(NSLayoutRelation)topRelation pinBottom:(BOOL)pinBottom bottomConstant:(CGFloat)bottomConstant bottomRelation:(NSLayoutRelation)bottomRelation pinLeft:(BOOL)pinLeft leftConstant:(CGFloat)leftConstant leftRelation:(NSLayoutRelation)leftRelation pinRight:(BOOL)pinRight rightConstant:(CGFloat)rightConstant rightRelation:(NSLayoutRelation)rightRelation { UIView *superview = subview.superview; NSMutableDictionary *constraintDic = [[NSMutableDictionary alloc] init]; if (pinTop) { - NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTop multiplier:1 constant:topConstant]; + NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:topRelation toItem:superview attribute:NSLayoutAttributeTop multiplier:1 constant:topConstant]; top.priority = 999; top.active = YES; [constraintDic setObject:top forKey:ConstraintTop]; } if (pinBottom) { - NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:subview attribute:NSLayoutAttributeBottom multiplier:1 constant:bottomConstant]; + NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeBottom relatedBy:bottomRelation toItem:subview attribute:NSLayoutAttributeBottom multiplier:1 constant:bottomConstant]; bottom.priority = 999; bottom.active = YES; [constraintDic setObject:bottom forKey:ConstraintBot]; } if (pinLeft) { - NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeLeading multiplier:1 constant:leftConstant]; + NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeLeading relatedBy:leftRelation toItem:superview attribute:NSLayoutAttributeLeading multiplier:1 constant:leftConstant]; leading.priority = 999; leading.active = YES; [constraintDic setObject:leading forKey:ConstraintLeading]; } if (pinRight) { - NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:subview attribute:NSLayoutAttributeTrailing multiplier:1 constant:rightConstant]; + NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeTrailing relatedBy:rightRelation toItem:subview attribute:NSLayoutAttributeTrailing multiplier:1 constant:rightConstant]; trailing.priority = 999; trailing.active = YES; [constraintDic setObject:trailing forKey:ConstraintTrailing]; @@ -152,6 +160,14 @@ NSString *const ConstraintWidth = @"width"; #pragma mark - With Margins ++ (nonnull NSDictionary *)pinViewToSuperview:(nonnull UIView *)subview useMargins:(BOOL)useMargins { + return @{ConstraintTop:[self pinViewTopToSuperview:subview useMargins:useMargins constant:0], + ConstraintLeading:[self pinViewLeftToSuperview:subview useMargins:useMargins constant:0], + ConstraintTrailing:[self pinViewRightToSuperview:subview useMargins:useMargins constant:0], + ConstraintBot:[self pinViewBottomToSuperview:subview useMargins:useMargins constant:0], + }; +} + + (NSLayoutConstraint *)pinViewTopToSuperview:(UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant { return [view.topAnchor constraintEqualToAnchor:(useMargins ? view.superview.layoutMarginsGuide.topAnchor : view.superview.topAnchor) constant:constant]; } diff --git a/MVMCoreUI/Molecules/MoleculeStackView.swift b/MVMCoreUI/Molecules/MoleculeStackView.swift index a43a6a94..10e048d3 100644 --- a/MVMCoreUI/Molecules/MoleculeStackView.swift +++ b/MVMCoreUI/Molecules/MoleculeStackView.swift @@ -57,8 +57,7 @@ public class MoleculeStackView: MFView { // Create the molecules and set the json. var moleculesArray = [] as [UIView] for moleculeJSON in molecules { - if let name = moleculeJSON.optionalStringForKey("moleculeName"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForName(name) { - molecule.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) + if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject) { moleculesArray.append(molecule) } } diff --git a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift index 21b05a22..4cd0dfd5 100644 --- a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift @@ -10,11 +10,42 @@ import UIKit @objcMembers open class MoleculeTableViewCell: UITableViewCell, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol { var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupView() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupView() + } + + // For the accessory view convenience. + var caretView: CaretView? + private var caretViewWidthSizeObject: MFSizeObject? + private var caretViewHeightSizeObject: MFSizeObject? public func updateView(_ size: CGFloat) { + MFStyler.setDefaultMarginsFor(self, size: size) + if #available(iOS 11.0, *) { + contentView.directionalLayoutMargins = directionalLayoutMargins + } else { + contentView.layoutMargins = layoutMargins + } + if let molecule = molecule as? MVMCoreViewProtocol { molecule.updateView(size) } + if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject { + caretView.frame = CGRect(x: 0, y: 0, width: widthObject.getValueBased(onSize: size), height: heightObject.getValueBased(onSize: size)) + } + } + + public func setupView() { + preservesSuperviewLayoutMargins = false + contentView.preservesSuperviewLayoutMargins = false + selectionStyle = .none } public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { @@ -22,13 +53,28 @@ import UIKit return } if molecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(json, delegateObject: delegateObject) { - addSubview(moleculeView) - NSLayoutConstraint.constraintPinSubview(toSuperview: moleculeView) + if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: json, delegateObject: delegateObject) { + contentView.addSubview(moleculeView) + NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: moleculeView.needsToBeConstrained?() ?? false).values)) molecule = moleculeView } } else { molecule?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } + backgroundColor = molecule?.backgroundColor + } + + // MARK- Convenience + public func addCaretViewAccessory() { + guard accessoryView == nil else { + return + } + let width: CGFloat = 6 + let height: CGFloat = 10 + caretView = CaretView(lineThickness: CaretView.thin) + caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height) + caretViewWidthSizeObject = MFSizeObject(scalingStandardSize: width) + caretViewHeightSizeObject = MFSizeObject(scalingStandardSize: height) + accessoryView = caretView } } diff --git a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h b/MVMCoreUI/Molecules/Protocols/MVMCoreUIMoleculeViewProtocol.h similarity index 79% rename from MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h rename to MVMCoreUI/Molecules/Protocols/MVMCoreUIMoleculeViewProtocol.h index cce08641..12557967 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h +++ b/MVMCoreUI/Molecules/Protocols/MVMCoreUIMoleculeViewProtocol.h @@ -22,6 +22,12 @@ // Notifies the creator that the view needs to be constrained in a view. - (BOOL)needsToBeConstrained; +// The alignment for the molecule if constrained. +- (UIStackViewAlignment)moleculeAlignment; + +// For the molecule list to load more efficiently. ++ (CGFloat)estimatedHeightForRow; + @end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.h b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.h index a79ca208..43a113c9 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.h @@ -22,4 +22,7 @@ - (nullable UIView *)getMoleculeForName:(nonnull NSString *)name; - (nullable UIView *)getMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable DelegateObject *)delegateObject; +// Similar to above but also checks if the molecule needs to be constrained for a stack. +- (nullable UIView *)getMoleculeForStackWithJSON:(nonnull NSDictionary *)json delegateObject:(nullable DelegateObject *)delegateObject; + @end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index f12179bc..9deb6cd5 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -45,20 +45,17 @@ - (nullable UIView *)getMoleculeForName:(nonnull NSString *)name { Class class = [self.moleculeMapping objectForKey:name]; - if (class) { - UIView *view = [[class alloc] init]; - if ([view respondsToSelector:@selector(needsToBeConstrained)] && [view needsToBeConstrained]) { - view = [[ViewConstrainingView alloc] initWithMolecule:view]; - } - if ([view respondsToSelector:@selector(setAsMolecule)]) { - [view setAsMolecule]; - } - return view; + if (!class) { + return nil; } - return nil; + UIView *molecule = [[class alloc] init]; + if ([molecule respondsToSelector:@selector(setAsMolecule)]) { + [molecule setAsMolecule]; + } + return molecule; } -- (nullable UIView *)getMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject { +- (nullable UIView *)getMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable DelegateObject *)delegateObject { NSString *moleculeName = [json string:@"moleculeName"]; if (!moleculeName) { return nil; @@ -69,4 +66,17 @@ } +- (nullable UIView *)getMoleculeForStackWithJSON:(nonnull NSDictionary *)json delegateObject:(nullable DelegateObject *)delegateObject { + NSString *moleculeName = [json string:@"moleculeName"]; + if (!moleculeName) { + return nil; + } + UIView *molecule = [self getMoleculeForName:moleculeName]; + if ([molecule respondsToSelector:@selector(needsToBeConstrained)] && [molecule needsToBeConstrained]) { + molecule = [[ViewConstrainingView alloc] initWithMolecule:molecule alignment:[molecule respondsToSelector:@selector(moleculeAlignment)] ? [molecule moleculeAlignment] : UIStackViewAlignmentFill]; + } + [molecule setWithJSON:json delegateObject:delegateObject additionalData:nil]; + return molecule; +} + @end diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index f1226056..ba6e7f15 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -23,14 +23,14 @@ public class MoleculeListTemplate: ThreeLayerTableViewController { } public override func viewForTop() -> UIView { - guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return super.viewForTop() } return molecule } override public func viewForBottom() -> UIView { - guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return viewForBottom() } return molecule @@ -41,6 +41,14 @@ public class MoleculeListTemplate: ThreeLayerTableViewController { registerWithTable() } + public override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + if let moleculeName = loadObject?.pageJSON?.stringOptionalWithChainOfKeysOrIndexes(["molecules",indexPath.row,"moleculeName"]), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping?[moleculeName] as? MVMCoreUIMoleculeViewProtocol.Type, + let estimatedHeightForRow = theClass.estimatedHeightForRow { + return estimatedHeightForRow() + } + return 0 + } + public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return loadObject?.pageJSON?.arrayForKey("molecules").count ?? 0 } diff --git a/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift b/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift index bdd187fe..b359ecf5 100644 --- a/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift @@ -18,7 +18,7 @@ public class MoleculeStackCenteredTemplate: ThreeLayerViewController { public override func viewForTop() -> UIView? { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return nil } return molecule @@ -26,7 +26,7 @@ public class MoleculeStackCenteredTemplate: ThreeLayerViewController { override public func viewForBottom() -> UIView? { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return nil } return molecule diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index 8b3d652d..51b3ed7f 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -16,7 +16,7 @@ public class MoleculeStackTemplate: ThreeLayerViewController { } public override func viewForTop() -> UIView? { - guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return nil } return molecule @@ -30,7 +30,7 @@ public class MoleculeStackTemplate: ThreeLayerViewController { } override public func viewForBottom() -> UIView? { - guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForJSON(moleculeJSON, delegateObject: delegateObject()) else { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return nil } return molecule From 2d73f89fd45007e510e6cc790bc2f69e7596b67e Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 30 Apr 2019 10:34:25 -0400 Subject: [PATCH 7/7] separator keys clean up --- .../ThreeLayerTableViewController.swift | 14 +-- MVMCoreUI/Molecules/MoleculeStackView.swift | 5 +- .../Molecules/MoleculeTableViewCell.swift | 86 +++++++++++++++++-- .../MVMCoreUIMoleculeMappingObject.m | 4 +- .../Templates/MoleculeListTemplate.swift | 32 +++---- MVMCoreUI/Utility/MVMCoreUIConstants.h | 3 + MVMCoreUI/Utility/MVMCoreUIConstants.m | 3 + 7 files changed, 115 insertions(+), 32 deletions(-) diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index a44fc4ee..85127a45 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -21,7 +21,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { private var topViewBottomConstraint: NSLayoutConstraint? private var bottomViewTopConstraint: NSLayoutConstraint? - //MARK:-MVMCoreViewProtocol + //MARK: - MVMCoreViewProtocol open override func updateViews() { super.updateViews() let width = view.bounds.width @@ -37,7 +37,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { self.tableView?.reloadData() } - //MARK:-MFViewController + //MARK: - MFViewController open override func newDataBuildScreen() { super.newDataBuildScreen() createViewForTableHeader() @@ -51,7 +51,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { // Do any additional setup after loading the view. } - //MARK:-Spacing + //MARK: - Spacing // If both are subclassed to return a value, then the buttons will not be pinned towards the bottom because neither spacing would try to fill the screen. /// Space between the top view and the table sections, nil to fill. 0 default open func spaceBelowTopView() -> CGFloat? { @@ -127,7 +127,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { } } - //MARK:-Header Footer + //MARK: - Header Footer /// Gets the top view and adds it to a spacing view, headerView, and then calls showHeader. open func createViewForTableHeader() { let topView = viewForTop() @@ -215,7 +215,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { } } - //MARK:-Functions to subclass + //MARK: - Functions to subclass /// Subclass for a top view. open func viewForTop() -> UIView { let view = MVMCoreUICommonViewsUtility.commonView() @@ -230,7 +230,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { return view } - //MARK:-Scrollview + //MARK: - Scrollview open override func scrollViewDidScroll(_ scrollView: UIScrollView) { // To stop handscroll animation if animating after scroll stopHandScrollAnimation(true) @@ -240,7 +240,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { tableView?.delegate = nil } - //MARK:-Animation + //MARK: - Animation open override func setupIntroAnimations() { if let topView = topView, topView.subviews.count > 0 { introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: topView)) diff --git a/MVMCoreUI/Molecules/MoleculeStackView.swift b/MVMCoreUI/Molecules/MoleculeStackView.swift index 10e048d3..ef060718 100644 --- a/MVMCoreUI/Molecules/MoleculeStackView.swift +++ b/MVMCoreUI/Molecules/MoleculeStackView.swift @@ -13,6 +13,7 @@ public class MoleculeStackView: MFView { var moleculesArray: [UIView]? var useMargins: Bool = false + // MARK: - Inits public override init(frame: CGRect) { super.init(frame: frame) } @@ -31,6 +32,7 @@ public class MoleculeStackView: MFView { fatalError("init(coder:) has not been implemented") } + // MARK: - MFViewProtocol public override func setupView() { super.setupView() translatesAutoresizingMaskIntoConstraints = false @@ -48,9 +50,10 @@ public class MoleculeStackView: MFView { } } + // MARK: - MVMCoreUIMoleculeViewProtocol open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - guard let molecules = json?.arrayForKey("molecules") as? [[String: Any]] else { + guard let molecules = json?.arrayForKey(KeyMolecules) as? [[String: Any]] else { return } diff --git a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift index 4cd0dfd5..1df0e67d 100644 --- a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift @@ -9,8 +9,24 @@ import UIKit @objcMembers open class MoleculeTableViewCell: UITableViewCell, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol { - var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + // For the accessory view convenience. + public var caretView: CaretView? + private var caretViewWidthSizeObject: MFSizeObject? + private var caretViewHeightSizeObject: MFSizeObject? + + // For separation between cells. + public var topSeparatorView: SeparatorView? + public var bottomSeparatorView: SeparatorView? + public enum SeparatorFrequency: String { + case All = "all" + case AllExceptTop = "allExceptTop" + case AllExceptBottom = "allExceptBottom" + case Between = "between" + } + + // MARK: - Inits public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupView() @@ -21,17 +37,17 @@ import UIKit setupView() } - // For the accessory view convenience. - var caretView: CaretView? - private var caretViewWidthSizeObject: MFSizeObject? - private var caretViewHeightSizeObject: MFSizeObject? - + // MARK: - MFViewProtocol public func updateView(_ size: CGFloat) { MFStyler.setDefaultMarginsFor(self, size: size) if #available(iOS 11.0, *) { contentView.directionalLayoutMargins = directionalLayoutMargins + topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) + bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) } else { contentView.layoutMargins = layoutMargins + topSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) + bottomSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) } if let molecule = molecule as? MVMCoreViewProtocol { @@ -40,6 +56,8 @@ import UIKit if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject { caretView.frame = CGRect(x: 0, y: 0, width: widthObject.getValueBased(onSize: size), height: heightObject.getValueBased(onSize: size)) } + topSeparatorView?.updateView(size) + bottomSeparatorView?.updateView(size) } public func setupView() { @@ -48,6 +66,7 @@ import UIKit selectionStyle = .none } + // MARK: - MVMCoreUIMoleculeViewProtocol public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { guard let json = json else { return @@ -64,7 +83,8 @@ import UIKit backgroundColor = molecule?.backgroundColor } - // MARK- Convenience + // MARK: - Convenience + /// Adds the standard mvm style caret to the accessory view public func addCaretViewAccessory() { guard accessoryView == nil else { return @@ -77,4 +97,56 @@ import UIKit caretViewHeightSizeObject = MFSizeObject(scalingStandardSize: height) accessoryView = caretView } + + func addSeparatorsIfNeeded() { + if topSeparatorView == nil { + topSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionTop) + topSeparatorView?.hide() + } + if bottomSeparatorView == nil { + bottomSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot) + bottomSeparatorView?.hide() + } + } + + /// For when the separator between cells shows using json and frequency. + public func setSeparatorWithJSON(_ json: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?, indexPath: IndexPath) { + guard let json = json else { + return + } + addSeparatorsIfNeeded() + topSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + bottomSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + if let separatorFrequencyString = json.optionalStringForKey("frequency"), let separatorFrequency = SeparatorFrequency(rawValue: separatorFrequencyString) { + setSeparatorFrequency(separatorFrequency, indexPath: indexPath) + } + } + + /// For when the separator between cells shows. + public func setSeparatorFrequency(_ separatorFrequency: SeparatorFrequency, indexPath: IndexPath) { + switch separatorFrequency { + case .All: + if indexPath.row == 0 { + topSeparatorView?.show() + } else { + topSeparatorView?.hide() + } + bottomSeparatorView?.show() + case .AllExceptBottom: + topSeparatorView?.show() + bottomSeparatorView?.hide() + case .Between: + if indexPath.row == 0 { + topSeparatorView?.hide() + } else { + topSeparatorView?.show() + } + bottomSeparatorView?.hide() + case .AllExceptTop: + fallthrough + default: + topSeparatorView?.hide() + bottomSeparatorView?.show() + } + } } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 9deb6cd5..66039753 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -56,7 +56,7 @@ } - (nullable UIView *)getMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable DelegateObject *)delegateObject { - NSString *moleculeName = [json string:@"moleculeName"]; + NSString *moleculeName = [json string:KeyMoleculeName]; if (!moleculeName) { return nil; } @@ -67,7 +67,7 @@ - (nullable UIView *)getMoleculeForStackWithJSON:(nonnull NSDictionary *)json delegateObject:(nullable DelegateObject *)delegateObject { - NSString *moleculeName = [json string:@"moleculeName"]; + NSString *moleculeName = [json string:KeyMoleculeName]; if (!moleculeName) { return nil; } diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index ba6e7f15..fe891a93 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -8,58 +8,60 @@ import UIKit -public class MoleculeListTemplate: ThreeLayerTableViewController { +open class MoleculeListTemplate: ThreeLayerTableViewController { - public override func registerWithTable() { + open override func registerWithTable() { super.registerWithTable() - guard let molecules = loadObject?.pageJSON?.arrayForKey("molecules") else { + guard let molecules = loadObject?.pageJSON?.arrayForKey(KeyMolecules) else { return } for case let molecule as Dictionary in molecules { - if let moleculeName = molecule.optionalStringForKey("moleculeName") { + if let moleculeName = molecule.optionalStringForKey(KeyMoleculeName) { tableView?.register(MoleculeTableViewCell.self, forCellReuseIdentifier: moleculeName) } } } - public override func viewForTop() -> UIView { + open override func viewForTop() -> UIView { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return super.viewForTop() } return molecule } - override public func viewForBottom() -> UIView { + override open func viewForBottom() -> UIView { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeForStack(withJSON: moleculeJSON, delegateObject: delegateObject()) else { return viewForBottom() } return molecule } - public override func newDataBuildScreen() { + open override func newDataBuildScreen() { super.newDataBuildScreen() registerWithTable() } - public override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - if let moleculeName = loadObject?.pageJSON?.stringOptionalWithChainOfKeysOrIndexes(["molecules",indexPath.row,"moleculeName"]), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping?[moleculeName] as? MVMCoreUIMoleculeViewProtocol.Type, + open override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + if let moleculeName = loadObject?.pageJSON?.stringOptionalWithChainOfKeysOrIndexes([KeyMolecules,indexPath.row,KeyMoleculeName]), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping?[moleculeName] as? MVMCoreUIMoleculeViewProtocol.Type, let estimatedHeightForRow = theClass.estimatedHeightForRow { return estimatedHeightForRow() } return 0 } - public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return loadObject?.pageJSON?.arrayForKey("molecules").count ?? 0 + open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return loadObject?.pageJSON?.arrayForKey(KeyMolecules).count ?? 0 } - public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let molecule = loadObject?.pageJSON?.optionalDictionaryWithChainOfKeysOrIndexes(["molecules",indexPath.row]), - let moleculeName = molecule.optionalStringForKey("moleculeName"), + open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let molecule = loadObject?.pageJSON?.optionalDictionaryWithChainOfKeysOrIndexes([KeyMolecules,indexPath.row]), + let moleculeName = molecule.optionalStringForKey(KeyMoleculeName), let cell = tableView.dequeueReusableCell(withIdentifier: moleculeName) as? MoleculeTableViewCell else { return UITableViewCell() } - cell.setWithJSON(molecule, delegateObject: delegateObject(), additionalData: nil) + let delegate = delegateObject() + cell.setWithJSON(molecule, delegateObject: delegate, additionalData: nil) + cell.setSeparatorWithJSON(loadObject?.pageJSON?.optionalDictionaryForKey("separator"), delegateObject: delegate, additionalData: nil, indexPath: indexPath) cell.updateView(tableView.bounds.width) return cell } diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.h b/MVMCoreUI/Utility/MVMCoreUIConstants.h index 93f77713..a1d2759a 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.h +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.h @@ -13,6 +13,9 @@ extern NSString * const KeyScreenHeading; +extern NSString * const KeyMolecules; +extern NSString * const KeyMoleculeName; + extern NSString * const KeyDisableButton; extern NSString * const KeyValue; diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.m b/MVMCoreUI/Utility/MVMCoreUIConstants.m index 684f013b..76949ccf 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.m +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.m @@ -12,6 +12,9 @@ NSString * const KeyScreenHeading = @"screenHeading"; +NSString * const KeyMolecules = @"molecules"; +NSString * const KeyMoleculeName = @"moleculeName"; + NSString * const KeyDisableButton = @"disableAction"; NSString * const KeyValue = @"value";