From 7f01bc286c46b0d739db6bb04b0a706a7dbb88b0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 13 Dec 2019 17:07:10 -0500 Subject: [PATCH] stack item as container --- MVMCoreUI.xcodeproj/project.pbxproj | 4 + MVMCoreUI/Containers/Container.swift | 32 ++++-- MVMCoreUI/Molecules/Items/StackItem.swift | 56 ++++++++++ MVMCoreUI/Molecules/MoleculeContainer.swift | 9 +- MVMCoreUI/Molecules/StandardFooterView.swift | 6 ++ .../EyebrowHeadlineBodyLink.swift | 12 +-- MVMCoreUI/Organisms/MoleculeStackView.swift | 101 +++++++----------- .../Templates/MoleculeStackTemplate.swift | 1 + 8 files changed, 137 insertions(+), 84 deletions(-) create mode 100644 MVMCoreUI/Molecules/Items/StackItem.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 73ecaabf..e8c26f4c 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -199,6 +199,7 @@ D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; + D2FB151D23A40F1500C20E10 /* StackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* StackItem.swift */; }; DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; @@ -404,6 +405,7 @@ D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = ""; }; D2F4DDE52371A4CB00CD28BB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = ""; }; + D2FB151C23A40F1500C20E10 /* StackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItem.swift; sourceTree = ""; }; DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = ""; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; @@ -524,6 +526,7 @@ D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */, D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */, D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */, + D2FB151C23A40F1500C20E10 /* StackItem.swift */, ); path = Items; sourceTree = ""; @@ -1076,6 +1079,7 @@ D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, + D2FB151D23A40F1500C20E10 /* StackItem.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, diff --git a/MVMCoreUI/Containers/Container.swift b/MVMCoreUI/Containers/Container.swift index 2af6e46a..f5bddcb9 100644 --- a/MVMCoreUI/Containers/Container.swift +++ b/MVMCoreUI/Containers/Container.swift @@ -9,10 +9,10 @@ import UIKit public protocol ContainerModelProtocol { - var horizontalAlignment: UIStackView.Alignment { get set } - var verticalAlignment: UIStackView.Alignment { get set } - var useHorizontalMargins: Bool { get set } - var useVerticalMargins: Bool { get set } + var horizontalAlignment: UIStackView.Alignment? { get set } + var verticalAlignment: UIStackView.Alignment? { get set } + var useHorizontalMargins: Bool? { get set } + var useVerticalMargins: Bool? { get set } } public class ContainerHelper: NSObject { @@ -146,8 +146,12 @@ public class ContainerHelper: NSObject { } func set(with model: ContainerModelProtocol) { - alignHorizontal(model.horizontalAlignment) - alignVertical(model.verticalAlignment) + if let horizontalAlignment = model.horizontalAlignment { + alignHorizontal(horizontalAlignment) + } + if let verticalAlignment = model.verticalAlignment { + alignVertical(verticalAlignment) + } } static func getAlignment(for string: String) -> UIStackView.Alignment? { @@ -188,6 +192,17 @@ public extension Container { super.setupView() backgroundColor = .clear } + + func addAndContain(_ view: UIView) { + addSubview(view) + containerHelper.constrainView(view) + self.view = view + } + + convenience init(andContain view: UIView) { + self.init() + addAndContain(view) + } } // MARK: - MVMCoreUIMoleculeViewProtocol @@ -207,4 +222,9 @@ public extension Container { containerHelper.alignVertical(alignment) } } + + override func reset() { + super.reset() + (view as? MVMCoreUIMoleculeViewProtocol)?.reset?() + } } diff --git a/MVMCoreUI/Molecules/Items/StackItem.swift b/MVMCoreUI/Molecules/Items/StackItem.swift new file mode 100644 index 00000000..72c24204 --- /dev/null +++ b/MVMCoreUI/Molecules/Items/StackItem.swift @@ -0,0 +1,56 @@ +// +// StackItem.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 12/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class StackItemModel: ContainerModelProtocol { + public var view: StackItem + public var spacing: CGFloat? + public var percentage: Int? + public var verticalAlignment: UIStackView.Alignment? + public var horizontalAlignment: UIStackView.Alignment? + public var useHorizontalMargins: Bool? + public var useVerticalMargins: Bool? + public var gone = false + + init(with view: StackItem) { + self.view = view + view.model = self + } + + init(with view: StackItem, json: [AnyHashable: Any]?) { + self.view = view + view.model = self + update(with: json) + } + + func update(with json: [AnyHashable: Any]?) { + gone = json?.boolForKey("gone") ?? (json == nil) + spacing = json?.optionalCGFloatForKey("spacing") + percentage = json?["percent"] as? Int + if let horizontalAlignmentString = json?.optionalStringForKey("horizontalAlignment") { + horizontalAlignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) + } else { + horizontalAlignment = nil + } + + if let verticalAlignmentString = json?.optionalStringForKey("verticalAlignment") { + verticalAlignment = ContainerHelper.getAlignment(for: verticalAlignmentString) + } else { + verticalAlignment = nil + } + + useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") ?? false + useVerticalMargins = json?.optionalBoolForKey("useVerticalMargins") ?? false + } +} + +open class StackItem: MoleculeContainer { + + +} diff --git a/MVMCoreUI/Molecules/MoleculeContainer.swift b/MVMCoreUI/Molecules/MoleculeContainer.swift index fa1095db..14a5bb4a 100644 --- a/MVMCoreUI/Molecules/MoleculeContainer.swift +++ b/MVMCoreUI/Molecules/MoleculeContainer.swift @@ -17,18 +17,11 @@ open class MoleculeContainer: Container { } if view == nil { if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: false) { - addSubview(molecule) - containerHelper.constrainView(molecule) - view = molecule + addAndContain(molecule) } } else { (view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) } super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } - - open override func reset() { - super.reset() - (view as? MVMCoreUIMoleculeViewProtocol)?.reset?() - } } diff --git a/MVMCoreUI/Molecules/StandardFooterView.swift b/MVMCoreUI/Molecules/StandardFooterView.swift index 9620e575..a83611cc 100644 --- a/MVMCoreUI/Molecules/StandardFooterView.swift +++ b/MVMCoreUI/Molecules/StandardFooterView.swift @@ -21,4 +21,10 @@ open class StandardFooterView: MoleculeContainer { } return 42 } + + open override func reset() { + super.reset() + topMarginPadding = PaddingDefaultVerticalSpacing + bottomMarginPadding = PaddingDefaultVerticalSpacing + } } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index a7d96a34..5bec3a5c 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -22,15 +22,14 @@ import UIKit return } stack.spacing = 0 - stack.updateViewHorizontalDefaults = false addSubview(stack) pinView(toSuperView: stack) - stack.addStackItem(StackItem(with: eyebrow), lastItem: false) - stack.addStackItem(StackItem(with: headline), lastItem: false) - stack.addStackItem(StackItem(with: body), lastItem: false) + stack.addStackItem(StackItemModel(with: StackItem(andContain: eyebrow)), lastItem: false) + stack.addStackItem(StackItemModel(with: StackItem(andContain: headline)), lastItem: false) + stack.addStackItem(StackItemModel(with: StackItem(andContain: body)), lastItem: false) // To visually take into account the extra padding in the intrinsic content of a button. - let stackItem = StackItem(with: link) + let stackItem = StackItemModel(with: StackItem(andContain: link)) stackItem.spacing = -6 stack.addStackItem(stackItem, lastItem: true) } @@ -38,6 +37,8 @@ import UIKit open override func updateView(_ size: CGFloat) { super.updateView(size) stack.updateView(size) + stack.directionalLayoutMargins.leading = 0 + stack.directionalLayoutMargins.trailing = 0 } // MARK: - MVMCoreUIMoleculeViewProtocol @@ -58,7 +59,6 @@ import UIKit super.reset() stack.reset() stack.spacing = 0 - stack.updateViewHorizontalDefaults = false eyebrow.styleB3(true) headline.styleB1(true) body.styleB2(true) diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index dc7cc81e..892243c8 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -8,47 +8,13 @@ import UIKit -public class StackItem { - var view: UIView - var spacing: CGFloat? - var percentage: Int? - var verticalAlignment: UIStackView.Alignment? - var horizontalAlignment: UIStackView.Alignment? - var gone = false - - init(with view: UIView) { - self.view = view - } - - init(with view: UIView, json: [AnyHashable: Any]?) { - self.view = view - update(with: json) - } - - func update(with json: [AnyHashable: Any]?) { - gone = json?.boolForKey("gone") ?? (json == nil) - spacing = json?.optionalCGFloatForKey("spacing") - percentage = json?["percent"] as? Int - if let alignment = json?.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"verticalAlignment"]) { - verticalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill) - } else { - verticalAlignment = nil - } - if let alignment = json?.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"horizontalAlignment"]) { - horizontalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill) - } else { - horizontalAlignment = nil - } - } -} - -public class MoleculeStackView: ViewConstrainingView { +open class MoleculeStackView: Container { var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() - var items: [StackItem] = [] + var items: [StackItemModel] = [] var useStackSpacingBeforeFirstItem = false - private var moleculesShouldSetHorizontalMargins = false - private var moleculesShouldSetVerticalMargins = false + var moleculesShouldSetHorizontalMargins = false + var moleculesShouldSetVerticalMargins = false /// For setting the direction of the stack var axis: NSLayoutConstraint.Axis = .vertical { @@ -97,6 +63,10 @@ public class MoleculeStackView: ViewConstrainingView { } // MARK: - Inits + public override init() { + super.init() + } + public override init(frame: CGRect) { super.init(frame: frame) } @@ -117,19 +87,20 @@ public class MoleculeStackView: ViewConstrainingView { return } MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) - updateViewHorizontalDefaults = true translatesAutoresizingMaskIntoConstraints = false backgroundColor = .clear addSubview(contentView) - pinView(toSuperView: contentView) + containerHelper.constrainView(contentView) contentView.setContentHuggingPriority(.defaultHigh, for: .vertical) contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal) } public override func updateView(_ size: CGFloat) { super.updateView(size) + directionalLayoutMargins.leading = 0 + directionalLayoutMargins.trailing = 0 for item in items { - (item.view as? MVMCoreViewProtocol)?.updateView(size) + item.view.updateView(size) } } @@ -137,11 +108,8 @@ public class MoleculeStackView: ViewConstrainingView { public override func reset() { super.reset() backgroundColor = .clear - updateViewHorizontalDefaults = true for item in items { - if let view = item.view as? MVMCoreUIMoleculeViewProtocol { - view.reset?() - } + item.view.reset() } } @@ -151,7 +119,7 @@ public class MoleculeStackView: ViewConstrainingView { removeAllItemViews() // If the items in the stack are the same, just update previous items instead of re-allocating. - var items: [StackItem]? + var items: [StackItemModel]? if MoleculeStackView.name(forReuse: previousJSON, delegateObject: delegateObject) == MoleculeStackView.name(forReuse: json, delegateObject: delegateObject) { items = self.items } @@ -169,22 +137,28 @@ public class MoleculeStackView: ViewConstrainingView { for (index, map) in molecules.enumerated() { if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule) { var view: UIView? + var stackItemModel: StackItemModel if let item = items?[index] { + stackItemModel = item item.update(with: map) view = item.view (view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: nil) addStackItem(item, lastItem: index == molecules.count - 1) - } else if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { - view = molecule - addStackItem(StackItem(with: molecule, json: map), lastItem: index == molecules.count - 1) + } else { + let stackItem = StackItem() + stackItem.setWithJSON(map, delegateObject: delegateObject, additionalData: additionalData) + view = stackItem + stackItemModel = StackItemModel(with: stackItem, json: map) + addStackItem(stackItemModel, lastItem: index == molecules.count - 1) } - (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(moleculesShouldSetHorizontalMargins) - (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(moleculesShouldSetVerticalMargins) + + stackItemModel.useHorizontalMargins = moleculesShouldSetHorizontalMargins + stackItemModel.useVerticalMargins = moleculesShouldSetVerticalMargins } } } - public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + public class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { // This will aggregate names of molecules to make an id. guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else { return "stack<>" @@ -199,7 +173,7 @@ public class MoleculeStackView: ViewConstrainingView { return name } - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return 0 } @@ -221,7 +195,7 @@ public class MoleculeStackView: ViewConstrainingView { return estimatedHeight } - public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return nil } @@ -237,11 +211,11 @@ public class MoleculeStackView: ViewConstrainingView { // MARK: - Adding to stack /// Adds the view to the stack. func addView(_ view: UIView, lastItem: Bool) { - addStackItem(StackItem(with: view), lastItem: lastItem) + addStackItem(StackItemModel(with: StackItem(andContain: view)), lastItem: lastItem) } /// Adds the stack item to the stack. - func addStackItem(_ stackItem: StackItem, lastItem: Bool) { + func addStackItem(_ stackItem: StackItemModel, lastItem: Bool) { guard !stackItem.gone else { items.append(stackItem) return @@ -251,12 +225,11 @@ public class MoleculeStackView: ViewConstrainingView { view.translatesAutoresizingMaskIntoConstraints = false let spacing = stackItem.spacing ?? self.spacing - if let view = view as? MVMCoreUIViewConstrainingProtocol { - let verticalAlignment = stackItem.verticalAlignment ?? (stackItem.percentage == nil && axis == .vertical ? .fill : (axis == .vertical ? .leading : .center)) - let horizontalAlignment = stackItem.horizontalAlignment ?? view.alignment?() ?? (axis == .vertical || stackItem.percentage == nil ? .fill : .leading) - view.alignHorizontal?(horizontalAlignment) - view.alignVertical?(verticalAlignment) - } + let verticalAlignment = stackItem.verticalAlignment ?? (stackItem.percentage == nil && axis == .vertical ? .fill : (axis == .vertical ? .leading : .center)) + let horizontalAlignment = stackItem.horizontalAlignment ?? (view.view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (axis == .vertical || stackItem.percentage == nil ? .fill : .leading) + view.containerHelper.alignHorizontal(horizontalAlignment) + view.containerHelper.alignVertical(verticalAlignment) + let first = items.first { !$0.gone } == nil if axis == .vertical { if first { @@ -295,10 +268,10 @@ public class MoleculeStackView: ViewConstrainingView { items.append(stackItem) } - func setWithStackItems(_ items: [StackItem]) { + func setWithStackItems(_ items: [StackItemModel]) { removeAllItemViews() self.items.removeAll() - var previousPresentItem: StackItem? = nil + var previousPresentItem: StackItemModel? = nil for item in items { if !item.gone { previousPresentItem = item diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index d6f78b05..b0881321 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -42,6 +42,7 @@ open class MoleculeStackTemplate: ThreeLayerViewController { } let stack = MoleculeStackView(frame: .zero) stack.useStackSpacingBeforeFirstItem = true + stack.moleculesShouldSetHorizontalMargins = true stack.setWithJSON(moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) return stack }