mvm_core_ui/MVMCoreUI/Molecules/MoleculeStackView.swift
2019-06-10 11:47:29 -04:00

190 lines
8.7 KiB
Swift

//
// MoleculeStackView.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 2/11/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
public class MoleculeStackView: ViewConstrainingView {
var spacingBlock: ((Any) -> UIEdgeInsets)?
var moleculesArray: [UIView]?
var useMargins: Bool = false
var alignment: UIStackView.Alignment = .fill
var contentView: UIView = MVMCoreUICommonViewsUtility.commonView()
private var spacingBetweenItems: [NSLayoutConstraint]?
/// For setting the direction of the stack
var axis: NSLayoutConstraint.Axis = .vertical {
didSet {
if axis != oldValue {
MVMCoreDispatchUtility.performBlock(onMainThread: {
// remove constraints
if self.axis == .vertical {
// layout vertical
} else {
// layout horizontal
}
})
}
}
}
/// For setting the alignment perpendicular to the direction of the stack. Default fill for vertical and center for horizontal. Can be overriden at the item level.
var itemAlignment: UIStackView.Alignment = .fill {
didSet {
if itemAlignment != oldValue {
MVMCoreDispatchUtility.performBlock(onMainThread: {
// remove constraints
if self.axis == .vertical {
// layout vertical
} else {
// layout horizontal
}
})
}
}
}
var spacing: Float = 16 {
didSet {
if spacing != oldValue {
MVMCoreDispatchUtility.performBlock(onMainThread: {
// loop space bettwen constraints and update. skip custom ones...
})
}
}
}
public func setAxisWithJSON(_ json: [AnyHashable: Any]?) {
switch json?.optionalStringForKey("axis") {
case "horizontal":
axis = .horizontal
default:
axis = .vertical
}
}
public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) {
let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant)
constraint.priority = priority
constraint.isActive = true
}
// MARK: - Inits
public override init(frame: CGRect) {
super.init(frame: frame)
}
public init(withJSON json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
super.init(frame: CGRect.zero)
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")
}
// MARK: - MFViewProtocol
public override func setupView() {
super.setupView()
guard contentView.superview == nil else {
return
}
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .clear
addConstrainedView(contentView)
//self.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical)
//self.setContentCompressionResistancePriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical)
}
public override func updateView(_ size: CGFloat) {
super.updateView(size)
for view in subviews {
if let mvmView = view as? MVMCoreViewProtocol {
mvmView.updateView(size)
}
}
}
// MARK: - MVMCoreUIMoleculeViewProtocol
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
MVMCoreUIStackableViewController.remove(contentView.subviews)
guard let molecules = json?.arrayForKey(KeyMolecules) as? [[String: Any]] else {
return
}
// Sets the stack attributes
setAxisWithJSON(json)
spacing = json?["spacing"] as? Float ?? 16
// Set the alignment for the stack in the containing view. The json driven value is for the axis direction alignment.
if axis == .vertical {
alignHorizontal(.fill)
alignVertical(ViewConstrainingView.getAlignmentFor(json?.optionalStringForKey("alignment"), defaultAlignment: .fill))
} else {
alignHorizontal(ViewConstrainingView.getAlignmentFor(json?.optionalStringForKey("alignment"), defaultAlignment: .fill))
alignVertical(.leading)
}
// Create the molecules and set the json.
var previousObject = contentView
for (index, map) in molecules.enumerated() {
if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) {
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(json?.optionalStringForKey("verticalAlignment"), defaultAlignment: (percent == nil && axis == .vertical ? UIStackView.Alignment.fill : UIStackView.Alignment.leading))
let horizontalAlignment = ViewConstrainingView.getAlignmentFor(json?.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 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)
}
}
}