From 486cbfdc3157f351a8883a71bfe89d5b49398d41 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 11 Jun 2019 17:13:05 -0400 Subject: [PATCH] clean up of stack --- MVMCoreUI/Molecules/MoleculeStackView.swift | 145 +++++++----------- .../Molecules/MoleculeTableViewCell.swift | 14 +- 2 files changed, 68 insertions(+), 91 deletions(-) diff --git a/MVMCoreUI/Molecules/MoleculeStackView.swift b/MVMCoreUI/Molecules/MoleculeStackView.swift index a0119dc3..6add4670 100644 --- a/MVMCoreUI/Molecules/MoleculeStackView.swift +++ b/MVMCoreUI/Molecules/MoleculeStackView.swift @@ -21,6 +21,10 @@ public class StackItem { init(with view: UIView, json: [AnyHashable: Any]) { self.view = view + update(with: json) + } + + func update(with json: [AnyHashable: Any]) { spacing = json.optionalCGFloatForKey("spacing") percentage = json["percent"] as? Int if let alignment = json.optionalStringForKey("verticalAlignment") { @@ -33,33 +37,29 @@ public class StackItem { } public class MoleculeStackView: ViewConstrainingView { - var spacingBlock: ((Any) -> UIEdgeInsets)? - var useMargins: Bool = false - var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() var items: [StackItem] = [] - private var spacingConstraints: [NSLayoutConstraint] = [] - /// For setting the direction of the stack var axis: NSLayoutConstraint.Axis = .vertical { didSet { + updateViewHorizontalDefaults = axis == .horizontal if axis != oldValue { restack() } } } + /// The spacing to use between each item in the stack. var spacing: CGFloat = 16 { didSet { if spacing != oldValue { - MVMCoreDispatchUtility.performBlock(onMainThread: { - // loop space bettwen constraints and update. skip custom ones... - }) + restack() } } } + // MARK: - Helpers public func setAxisWithJSON(_ json: [AnyHashable: Any]?) { switch json?.optionalStringForKey("axis") { case "horizontal": @@ -69,12 +69,20 @@ public class MoleculeStackView: ViewConstrainingView { } } - - public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) -> NSLayoutConstraint { + public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) { let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant) constraint.priority = priority constraint.isActive = true - return constraint + } + + /// Restacks the existing items. + func restack() { + MVMCoreUIStackableViewController.remove(contentView.subviews) + let stackItems = items + items.removeAll() + for (index, item) in stackItems.enumerated() { + addStackItem(item, lastItem: index == stackItems.count - 1) + } } // MARK: - Inits @@ -87,11 +95,6 @@ public class MoleculeStackView: ViewConstrainingView { setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } - public convenience init(withJSON json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, spacingBlock: ((Any) -> UIEdgeInsets)?) { - self.init(withJSON: json, delegateObject: delegateObject, additionalData: nil) - self.spacingBlock = spacingBlock - } - public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -111,15 +114,18 @@ public class MoleculeStackView: ViewConstrainingView { public override func updateView(_ size: CGFloat) { super.updateView(size) - for view in subviews { - if let mvmView = view as? MVMCoreViewProtocol { - mvmView.updateView(size) - } + for item in items { + (item.view as? MVMCoreViewProtocol)?.updateView(size) } } // MARK: - MVMCoreUIMoleculeViewProtocol + public override func setAsMolecule() { + updateViewHorizontalDefaults = false + } + public override func reset() { + backgroundColor = .clear for item in items { if let view = item.view as? MVMCoreUIMoleculeViewProtocol { view.reset?() @@ -128,9 +134,21 @@ public class MoleculeStackView: ViewConstrainingView { } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + let previousJSON = self.json super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - clear() + MVMCoreUIStackableViewController.remove(contentView.subviews) + + // If the items in the stack are the same, just update previous items instead of re-allocating. + var items: [StackItem]? + if MoleculeStackView.name(forReuse: previousJSON, delegateObject: delegateObject) == MoleculeStackView.name(forReuse: json, delegateObject: delegateObject) { + items = self.items + } + self.items = [] + if let colorString = json?.optionalStringForKey(KeyBackgroundColor) { + backgroundColor = .mfGet(forHex: colorString) + } + guard let molecules = json?.arrayForKey(KeyMolecules) as? [[String: Any]] else { return } @@ -148,59 +166,28 @@ public class MoleculeStackView: ViewConstrainingView { alignVertical(.leading) } - // Create the molecules and set the json. + // Adds the molecules and sets the json. for (index, map) in molecules.enumerated() { - if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { - - addStackItem(StackItem(with: molecule, json: map), lastItem: index == molecules.count - 1) - /*contentView.addSubview(molecule) - molecule.translatesAutoresizingMaskIntoConstraints = false - - let spacing = CGFloat(map["spacing"] as? Float ?? self.spacing) - let percent = map["percent"] as? Int - let verticalAlignment = ViewConstrainingView.getAlignmentFor(map.optionalStringForKey("verticalAlignment"), defaultAlignment: (percent == nil && axis == .vertical ? UIStackView.Alignment.fill : UIStackView.Alignment.leading)) - let horizontalAlignment = ViewConstrainingView.getAlignmentFor(map.optionalStringForKey("horizontalAlignment"), defaultAlignment: (axis == .vertical || percent == nil ? UIStackView.Alignment.fill : UIStackView.Alignment.leading)) - - if let molecule = molecule as? MVMCoreUIViewConstrainingProtocol { - molecule.alignHorizontal?(horizontalAlignment) - molecule.alignVertical?(verticalAlignment) + if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule) { + var view: UIView? + if let item = items?[index] { + (item.view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) + item.update(with: moleculeJSON) + view = item.view + addStackItem(item, lastItem: index == molecules.count - 1) + } else if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { + view = molecule + addStackItem(StackItem(with: molecule, json: map), lastItem: index == molecules.count - 1) } - if axis == .vertical { - if index == 0 { - pinView(molecule, toView: previousObject, attribute: .top, relation: .equal, priority: .required, constant: spacing) - } else { - NSLayoutConstraint(pinFirstView: previousObject, toSecondView: molecule, withConstant: spacing, directionVertical: true)?.isActive = true - } - pinView(molecule, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0) - pinView(contentView, toView: molecule, attribute: .trailing, relation: .equal, priority: .required, constant: 0) - if let percent = percent { - molecule.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0).isActive = true - } - } else { - if index == 0 { - pinView(molecule, toView: previousObject, attribute: .leading, relation: .equal, priority: .required, constant: spacing) - } else { - NSLayoutConstraint(pinFirstView: previousObject, toSecondView: molecule, withConstant: spacing, directionVertical: false)?.isActive = true - } - pinView(molecule, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) - pinView(contentView, toView: molecule, attribute: .bottom, relation: .equal, priority: .required, constant: 0) - if let percent = percent { - molecule.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0).isActive = true - } - } - previousObject = molecule - } - } - if axis == .vertical { - pinView(contentView, toView: previousObject, attribute: .bottom, relation: .equal, priority: .required, constant: 0) - } else { - pinView(contentView, toView: previousObject, attribute: .right, relation: .equal, priority: .required, constant: 0) - }*/ + + (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(axis == .vertical) + (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false) } } } public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + // This will aggregate names of molecules to make an id. var name = "" guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else { return name @@ -213,21 +200,7 @@ public class MoleculeStackView: ViewConstrainingView { return name } - // MARK: - Convenience Functions - func clear() { - MVMCoreUIStackableViewController.remove(contentView.subviews) - spacingConstraints = [] - } - - func restack() { - clear() - let stackItems = items - items.removeAll() - for (index, item) in stackItems.enumerated() { - addStackItem(item, lastItem: index == stackItems.count - 1) - } - } - + // MARK: - Adding to stack /// Adds the view to the stack. func addView(_ view: UIView, lastItem: Bool) { addStackItem(StackItem(with: view), lastItem: lastItem) @@ -248,9 +221,9 @@ public class MoleculeStackView: ViewConstrainingView { } if axis == .vertical { if items.count == 0 { - spacingConstraints.append(pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: spacing)) + pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: spacing) } else if let previousView = items.last?.view { - spacingConstraints.append(NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: true)) + _ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: true) } pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0) pinView(contentView, toView: view, attribute: .trailing, relation: .equal, priority: .required, constant: 0) @@ -263,9 +236,9 @@ public class MoleculeStackView: ViewConstrainingView { } else { if items.count == 0 { // First horizontal item has no spacing by default unless told otherwise. - spacingConstraints.append(pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: stackItem.spacing ?? 0)) + pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: stackItem.spacing ?? 0) } else if let previousView = items.last?.view { - spacingConstraints.append(NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: false)) + _ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: false) } pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) diff --git a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift index 10f3e15d..0f26a9b8 100644 --- a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift @@ -44,6 +44,7 @@ import UIKit MFStyler.setDefaultMarginsFor(self, size: size, horizontal: true, vertical: true) if #available(iOS 11.0, *) { if accessoryView != nil { + // Smaller left margin if accessory view. var margin = directionalLayoutMargins margin.trailing = 16 contentView.directionalLayoutMargins = margin @@ -54,6 +55,7 @@ import UIKit bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) } else { if accessoryView != nil { + // Smaller left margin if accessory view. var margin = layoutMargins margin.right = 16 contentView.layoutMargins = margin @@ -87,15 +89,17 @@ import UIKit if molecule == nil { if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { contentView.addSubview(moleculeView) - let standardConstraints = (moleculeView as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true + var standardConstraints = true + if let castView = moleculeView as? MVMCoreUIViewConstrainingProtocol { + standardConstraints = castView.useStandardConstraints?() ?? true + castView.shouldSetHorizontalMargins?(!standardConstraints) + castView.shouldSetVerticalMargins?(!standardConstraints) + } NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values)) if standardConstraints { let constraint = contentView.heightAnchor.constraint(equalToConstant: 80) constraint.priority = .defaultLow constraint.isActive = true - if let moleculeView = moleculeView as? ViewConstrainingView { - moleculeView.updateViewHorizontalDefaults = false - } } molecule = moleculeView } @@ -126,7 +130,7 @@ import UIKit guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON, delegateObject: nil), let estimatedHeightFor = theClass.estimatedHeight else { - return 0 + return 80 } return estimatedHeightFor(moleculeJSON) }