clean up of stack

This commit is contained in:
Pfeil, Scott Robert 2019-06-11 17:13:05 -04:00
parent 2221ab6672
commit 486cbfdc31
2 changed files with 68 additions and 91 deletions

View File

@ -21,6 +21,10 @@ public class StackItem {
init(with view: UIView, json: [AnyHashable: Any]) { init(with view: UIView, json: [AnyHashable: Any]) {
self.view = view self.view = view
update(with: json)
}
func update(with json: [AnyHashable: Any]) {
spacing = json.optionalCGFloatForKey("spacing") spacing = json.optionalCGFloatForKey("spacing")
percentage = json["percent"] as? Int percentage = json["percent"] as? Int
if let alignment = json.optionalStringForKey("verticalAlignment") { if let alignment = json.optionalStringForKey("verticalAlignment") {
@ -33,33 +37,29 @@ public class StackItem {
} }
public class MoleculeStackView: ViewConstrainingView { public class MoleculeStackView: ViewConstrainingView {
var spacingBlock: ((Any) -> UIEdgeInsets)?
var useMargins: Bool = false
var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() var contentView: UIView = MVMCoreUICommonViewsUtility.commonView()
var items: [StackItem] = [] var items: [StackItem] = []
private var spacingConstraints: [NSLayoutConstraint] = []
/// For setting the direction of the stack /// For setting the direction of the stack
var axis: NSLayoutConstraint.Axis = .vertical { var axis: NSLayoutConstraint.Axis = .vertical {
didSet { didSet {
updateViewHorizontalDefaults = axis == .horizontal
if axis != oldValue { if axis != oldValue {
restack() restack()
} }
} }
} }
/// The spacing to use between each item in the stack.
var spacing: CGFloat = 16 { var spacing: CGFloat = 16 {
didSet { didSet {
if spacing != oldValue { if spacing != oldValue {
MVMCoreDispatchUtility.performBlock(onMainThread: { restack()
// loop space bettwen constraints and update. skip custom ones...
})
} }
} }
} }
// MARK: - Helpers
public func setAxisWithJSON(_ json: [AnyHashable: Any]?) { public func setAxisWithJSON(_ json: [AnyHashable: Any]?) {
switch json?.optionalStringForKey("axis") { switch json?.optionalStringForKey("axis") {
case "horizontal": 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) {
public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant) let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant)
constraint.priority = priority constraint.priority = priority
constraint.isActive = true 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 // MARK: - Inits
@ -87,11 +95,6 @@ public class MoleculeStackView: ViewConstrainingView {
setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) 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) { public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -111,15 +114,18 @@ public class MoleculeStackView: ViewConstrainingView {
public override func updateView(_ size: CGFloat) { public override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
for view in subviews { for item in items {
if let mvmView = view as? MVMCoreViewProtocol { (item.view as? MVMCoreViewProtocol)?.updateView(size)
mvmView.updateView(size)
}
} }
} }
// MARK: - MVMCoreUIMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol
public override func setAsMolecule() {
updateViewHorizontalDefaults = false
}
public override func reset() { public override func reset() {
backgroundColor = .clear
for item in items { for item in items {
if let view = item.view as? MVMCoreUIMoleculeViewProtocol { if let view = item.view as? MVMCoreUIMoleculeViewProtocol {
view.reset?() view.reset?()
@ -128,9 +134,21 @@ public class MoleculeStackView: ViewConstrainingView {
} }
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
let previousJSON = self.json
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) 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 { guard let molecules = json?.arrayForKey(KeyMolecules) as? [[String: Any]] else {
return return
} }
@ -148,59 +166,28 @@ public class MoleculeStackView: ViewConstrainingView {
alignVertical(.leading) alignVertical(.leading)
} }
// Create the molecules and set the json. // Adds the molecules and sets the json.
for (index, map) in molecules.enumerated() { for (index, map) in molecules.enumerated() {
if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule) {
var view: UIView?
addStackItem(StackItem(with: molecule, json: map), lastItem: index == molecules.count - 1) if let item = items?[index] {
/*contentView.addSubview(molecule) (item.view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
molecule.translatesAutoresizingMaskIntoConstraints = false item.update(with: moleculeJSON)
view = item.view
let spacing = CGFloat(map["spacing"] as? Float ?? self.spacing) addStackItem(item, lastItem: index == molecules.count - 1)
let percent = map["percent"] as? Int } else if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) {
let verticalAlignment = ViewConstrainingView.getAlignmentFor(map.optionalStringForKey("verticalAlignment"), defaultAlignment: (percent == nil && axis == .vertical ? UIStackView.Alignment.fill : UIStackView.Alignment.leading)) view = molecule
let horizontalAlignment = ViewConstrainingView.getAlignmentFor(map.optionalStringForKey("horizontalAlignment"), defaultAlignment: (axis == .vertical || percent == nil ? UIStackView.Alignment.fill : UIStackView.Alignment.leading)) addStackItem(StackItem(with: molecule, json: map), lastItem: index == molecules.count - 1)
if let molecule = molecule as? MVMCoreUIViewConstrainingProtocol {
molecule.alignHorizontal?(horizontalAlignment)
molecule.alignVertical?(verticalAlignment)
} }
if axis == .vertical {
if index == 0 { (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(axis == .vertical)
pinView(molecule, toView: previousObject, attribute: .top, relation: .equal, priority: .required, constant: spacing) (view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false)
} 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)
}*/
} }
} }
} }
public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
// This will aggregate names of molecules to make an id.
var name = "" var name = ""
guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else { guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else {
return name return name
@ -213,21 +200,7 @@ public class MoleculeStackView: ViewConstrainingView {
return name return name
} }
// MARK: - Convenience Functions // MARK: - Adding to stack
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)
}
}
/// Adds the view to the stack. /// Adds the view to the stack.
func addView(_ view: UIView, lastItem: Bool) { func addView(_ view: UIView, lastItem: Bool) {
addStackItem(StackItem(with: view), lastItem: lastItem) addStackItem(StackItem(with: view), lastItem: lastItem)
@ -248,9 +221,9 @@ public class MoleculeStackView: ViewConstrainingView {
} }
if axis == .vertical { if axis == .vertical {
if items.count == 0 { 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 { } 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(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0)
pinView(contentView, toView: view, attribute: .trailing, 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 { } else {
if items.count == 0 { if items.count == 0 {
// First horizontal item has no spacing by default unless told otherwise. // 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 { } 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(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0)
pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0)

View File

@ -44,6 +44,7 @@ import UIKit
MFStyler.setDefaultMarginsFor(self, size: size, horizontal: true, vertical: true) MFStyler.setDefaultMarginsFor(self, size: size, horizontal: true, vertical: true)
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
if accessoryView != nil { if accessoryView != nil {
// Smaller left margin if accessory view.
var margin = directionalLayoutMargins var margin = directionalLayoutMargins
margin.trailing = 16 margin.trailing = 16
contentView.directionalLayoutMargins = margin contentView.directionalLayoutMargins = margin
@ -54,6 +55,7 @@ import UIKit
bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
} else { } else {
if accessoryView != nil { if accessoryView != nil {
// Smaller left margin if accessory view.
var margin = layoutMargins var margin = layoutMargins
margin.right = 16 margin.right = 16
contentView.layoutMargins = margin contentView.layoutMargins = margin
@ -87,15 +89,17 @@ import UIKit
if molecule == nil { if molecule == nil {
if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) {
contentView.addSubview(moleculeView) 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)) NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values))
if standardConstraints { if standardConstraints {
let constraint = contentView.heightAnchor.constraint(equalToConstant: 80) let constraint = contentView.heightAnchor.constraint(equalToConstant: 80)
constraint.priority = .defaultLow constraint.priority = .defaultLow
constraint.isActive = true constraint.isActive = true
if let moleculeView = moleculeView as? ViewConstrainingView {
moleculeView.updateViewHorizontalDefaults = false
}
} }
molecule = moleculeView molecule = moleculeView
} }
@ -126,7 +130,7 @@ import UIKit
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule),
let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON, delegateObject: nil), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON, delegateObject: nil),
let estimatedHeightFor = theClass.estimatedHeight else { let estimatedHeightFor = theClass.estimatedHeight else {
return 0 return 80
} }
return estimatedHeightFor(moleculeJSON) return estimatedHeightFor(moleculeJSON)
} }