From 396c22f2434b0c5eb94c42add833adf30b978f2c Mon Sep 17 00:00:00 2001 From: "Murugan, Vimal" Date: Sat, 11 Jan 2020 16:06:25 +0530 Subject: [PATCH 01/19] doughnut chart model added doughnut chart view added doughnut chart added --- MVMCoreUI.xcodeproj/project.pbxproj | 28 ++- .../Models/Molecules/DoughnutChartModel.swift | 85 +++++++++ MVMCoreUI/Molecules/DoughnutChart.swift | 179 ++++++++++++++++++ MVMCoreUI/Molecules/DoughnutChartView.swift | 179 ++++++++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 3 +- .../OtherHandlers/MoleculeObjectMapping.swift | 1 + 6 files changed, 466 insertions(+), 9 deletions(-) create mode 100644 MVMCoreUI/Models/Molecules/DoughnutChartModel.swift create mode 100644 MVMCoreUI/Molecules/DoughnutChart.swift create mode 100644 MVMCoreUI/Molecules/DoughnutChartView.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index dbee02e2..8efde844 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -67,6 +67,9 @@ 94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A623872DA90006CF46 /* LabelAttributeColorModel.swift */; }; 94C2D9A923872E5E0006CF46 /* LabelAttributeImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */; }; 94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */; }; + C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; }; + C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69523C990BC00BFB94E /* DoughnutChart.swift */; }; + C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69723C990C200BFB94E /* DoughnutChartView.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */; }; @@ -307,6 +310,9 @@ 94C2D9A623872DA90006CF46 /* LabelAttributeColorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeColorModel.swift; sourceTree = ""; }; 94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeImageModel.swift; sourceTree = ""; }; 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeActionModel.swift; sourceTree = ""; }; + C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = ""; }; + C695A69523C990BC00BFB94E /* DoughnutChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChart.swift; sourceTree = ""; }; + C695A69723C990C200BFB94E /* DoughnutChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartView.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraintAxis+Extension.swift"; sourceTree = ""; }; @@ -530,6 +536,7 @@ 01EB368723609801006832FA /* Molecules */ = { isa = PBXGroup; children = ( + C695A69323C9909000BFB94E /* DoughnutChartModel.swift */, 01EB368923609801006832FA /* ListItemModel.swift */, 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */, 01EB368B23609801006832FA /* MoleculeStackModel.swift */, @@ -542,6 +549,14 @@ path = Molecules; sourceTree = ""; }; + 0A5D59C323AD488600EFD9E9 /* Protocols */ = { + isa = PBXGroup; + children = ( + 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 0AA33B322398134B0067DD0F /* Primitive Models */ = { isa = PBXGroup; children = ( @@ -580,14 +595,6 @@ name = "Recovered References"; sourceTree = ""; }; - 0A5D59C323AD488600EFD9E9 /* Protocols */ = { - isa = PBXGroup; - children = ( - 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, - ); - path = Protocols; - sourceTree = ""; - }; D213347423842FE3008E41B3 /* Controllers */ = { isa = PBXGroup; children = ( @@ -785,6 +792,8 @@ D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */, + C695A69523C990BC00BFB94E /* DoughnutChart.swift */, + C695A69723C990C200BFB94E /* DoughnutChartView.swift */, ); path = Molecules; sourceTree = ""; @@ -1296,6 +1305,7 @@ D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, + C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */, 94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */, 017BEB4023620A230024EF95 /* TextFieldModel.swift in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, @@ -1396,6 +1406,7 @@ D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */, D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */, D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */, + C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */, D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */, D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */, @@ -1404,6 +1415,7 @@ 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */, 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, D22D1F1B220341F60077CEC0 /* MVMCoreUICheckBox.m in Sources */, + C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */, D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */, 946EE1BA237B66D80036751F /* ModelHelper.swift in Sources */, 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */, diff --git a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift new file mode 100644 index 00000000..f6a1efb8 --- /dev/null +++ b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift @@ -0,0 +1,85 @@ +// +// DoughnutChartModel.swift +// MVMCoreUI +// +// Created by Murugan, Vimal on 10/01/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class DoughnutChartModel: MoleculeProtocol { + + public var backgroundColor: String? + public static var identifier: String = "doughnutChart" + public var title: LabelModel? + public var subtitle: LabelModel? + public var sections: [ChartItemModel] + public var lineWidth: CGFloat = 30.0 + public var lineGap: CGFloat = 0.05 //For 3px gap + public var spaceRequired = true + + enum CodingKeys: String, CodingKey { + case list + case title + case subtitle + case sections + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + self.title = try typeContainer.decodeMoleculeIfPresent(codingKey: .title) as? LabelModel + self.subtitle = try typeContainer.decodeMoleculeIfPresent(codingKey: .subtitle) as? LabelModel + //self.sections = try typeContainer.decodeMolecules(codingKey: .sections) as! [ChartItemModel] + + //TODO: Hasve to check with scott + self.sections = [] + guard var container = try? typeContainer.nestedUnkeyedContainer(forKey: .sections) else { + return + } + var sections = [ChartItemModel]() + while !container.isAtEnd { + if let element = try container + .decodeIfPresent(ChartItemModel.self) { + sections.append(element) + } + } + self.sections = sections + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModelIfPresent(title, forKey: .title) + try container.encodeModelIfPresent(subtitle, forKey: .subtitle) + try container.encodeModels(sections as [Model], forKey: .sections) + } +} + + +@objcMembers public class ChartItemModel: MoleculeProtocol { + public var backgroundColor: String? + public var label: LabelModel + public var percent: CGFloat + public var color: String + public static var identifier: String = "chartItem" + + enum CodingKeys: String, CodingKey { + case label + case percent + case color + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + self.label = try typeContainer.decodeMolecule(codingKey: .label) as! LabelModel + self.percent = try typeContainer.decode(CGFloat.self, forKey: .percent) + self.color = try typeContainer.decode(String.self, forKey: .color) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModel(label, forKey: .label) + try container.encode(percent, forKey: .percent) + try container.encode(color, forKey: .color) + } +} diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift new file mode 100644 index 00000000..08bdefb1 --- /dev/null +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -0,0 +1,179 @@ +// +// DoughnutChart.swift +// MVMCoreUI +// +// Created by Murugan, Vimal on 07/01/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { + + var containerView = MVMCoreUICommonViewsUtility.commonView() + var containerLayer = CALayer() + var titleLabel = Label.commonLabelH3(true) + var subTitleLabel = Label.commonLabelB2(true) + var doughnutChartModel: DoughnutChartModel? { + get { return model as? DoughnutChartModel } + } + var labelContainer = ViewConstrainingView.empty() + var heightConstraint: NSLayoutConstraint? + + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! + //private var lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 30)! + + var height: CGFloat = 150 { + didSet { + if height != oldValue { + sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! + updateContainer() + drawGraph() + } + } + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + titleLabel.updateView(size) + subTitleLabel.updateView(size) + updateContainer() + drawGraph() + } + + open override func reset() { + super.reset() + titleLabel.reset() + subTitleLabel.reset() + clearLayers() + } + + public func setAsMolecule() { + titleLabel.setAsMolecule() + subTitleLabel.setAsMolecule() + } + + open override func setupView() { + super.setupView() + guard containerView.superview == nil else { + return + } + addSubview(containerView) + addSubview(labelContainer) + + labelContainer.addSubview(titleLabel) + labelContainer.addSubview(subTitleLabel) + titleLabel.textAlignment = .center + subTitleLabel.textAlignment = .center + + //Make label font size to adjust width if label content is high + titleLabel.numberOfLines = 1 + titleLabel.adjustsFontSizeToFitWidth = true + + containerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + containerView.topAnchor.constraint(equalTo: topAnchor).isActive = true + bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true + trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true + containerView.layer.addSublayer(containerLayer) + heightConstraint = containerView.heightAnchor.constraint(equalToConstant: + sizeObject.getValueBasedOnApplicationWidth()) + heightConstraint?.isActive = true + containerView.widthAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true + + labelContainer.leftPin = labelContainer.leftAnchor.constraint(greaterThanOrEqualTo: containerView.leftAnchor, constant: lineWidth()) + labelContainer.leftPin?.isActive = true + labelContainer.topPin = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: lineWidth()) + labelContainer.topPin?.isActive = true + labelContainer.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true + labelContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true + + NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true) + NSLayoutConstraint.constraintPinSubview(subTitleLabel, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true) + _ = NSLayoutConstraint(pinFirstView: titleLabel, toSecondView: subTitleLabel, withConstant: 0, directionVertical: true) + //Rotate view for initial draw + containerLayer.transform = CATransform3DMakeRotation(1 * .pi, 0.0, 0.0, 1.0) + + } + + open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + clearLayers() + guard let doughnutChartModel = model as? DoughnutChartModel else { + return + } + titleLabel.setWithModel(doughnutChartModel.title, delegateObject, additionalData) + subTitleLabel.setWithModel(doughnutChartModel.subtitle, delegateObject, additionalData) + titleLabel.textAlignment = .center + subTitleLabel.textAlignment = .center + //lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: doughnutChartModel.lineWidth)! + updateLabelContainer() + drawGraph() + } + + func drawGraph() { + clearLayers() + let widthHeight = sizeObject.getValueBasedOnApplicationWidth() + containerLayer.frame = CGRect(x: 0, y: 0, + width: widthHeight, + height: widthHeight) + if let doughnutChart = doughnutChartModel { + var prevPercent: CGFloat = 0.0 + //If Single item in array. We don't need gap + doughnutChartModel?.lineGap = (doughnutChart.sections.count == 1) ? 0.0 : doughnutChart.lineGap + for model in doughnutChart.sections { + prevPercent += drawBar(model, prevPercent, doughnutChart.spaceRequired, doughnutChart.lineGap) + } + } + } + + func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat, _ spaceRequired: Bool, _ lineGap: CGFloat) -> CGFloat { + + let shapeLayer = CAShapeLayer() + shapeLayer.frame = containerLayer.frame + shapeLayer.lineWidth = lineWidth() + //lineSizeObject.getValueBasedOnApplicationWidth() + shapeLayer.fillColor = nil + shapeLayer.strokeColor = UIColor.mfGet(forHex: chartModel.color).cgColor + + let arcCenter = shapeLayer.position + let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 + //lineSizeObject.getValueBasedOnApplicationWidth() / 2.0 + let clockwise = true + + let value: CGFloat = chartModel.percent + let gap: CGFloat = spaceRequired ? lineGap : 0.0 + let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) + let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap + let circlePath = UIBezierPath(arcCenter: arcCenter, + radius: radius, + startAngle: startAngle, + endAngle: endAngle, + clockwise: clockwise) + + shapeLayer.path = circlePath.cgPath + containerLayer.addSublayer(shapeLayer) + return value + } + + func clearLayers() { + containerLayer.sublayers?.forEach({ $0.removeFromSuperlayer() }) + } + + func updateContainer() { + heightConstraint?.constant = sizeObject.getValueBasedOnApplicationWidth() + updateLabelContainer() + setNeedsDisplay() + } + + func lineWidth() -> CGFloat { + return doughnutChartModel?.lineWidth ?? 30 + //lineSizeObject.getValueBasedOnApplicationWidth() + 5 + } + + func updateLabelContainer() { + labelContainer.leftPin?.constant = lineWidth() + labelContainer.topPin?.constant = lineWidth() + labelContainer.setNeedsDisplay() + } + +} diff --git a/MVMCoreUI/Molecules/DoughnutChartView.swift b/MVMCoreUI/Molecules/DoughnutChartView.swift new file mode 100644 index 00000000..ebe77200 --- /dev/null +++ b/MVMCoreUI/Molecules/DoughnutChartView.swift @@ -0,0 +1,179 @@ +// +// DoughnutChartView.swift +// MVMCoreUI +// +// Created by Murugan, Vimal on 26/12/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class DoughnutChartView: View { + + var doughnutChart = DoughnutChart(frame: CGRect.zero) + var colorLablesStack = ColorViewLabelsStack() + var container = MVMCoreUICommonViewsUtility.commonView() + var doughnutChartModel: DoughnutChartModel? { + get { return model as? DoughnutChartModel } + } + + open override func setupView() { + super.setupView() + guard container.superview == nil else { + return + } + + addSubview(container) + doughnutChart.translatesAutoresizingMaskIntoConstraints = false + container.addSubview(doughnutChart) + colorLablesStack.translatesAutoresizingMaskIntoConstraints = false + container.addSubview(colorLablesStack) + + NSLayoutConstraint.constraintPinSubview(container, pinTop: true, pinBottom: true, pinLeft: true, pinRight: true) + doughnutChart.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true + doughnutChart.topAnchor.constraint(equalTo: container.topAnchor, constant: PaddingFour).isActive = true + container.bottomAnchor.constraint(greaterThanOrEqualTo: doughnutChart.bottomAnchor).isActive = true + + let containerBottomAnchor = container.bottomAnchor.constraint(equalTo: doughnutChart.bottomAnchor) + containerBottomAnchor.priority = UILayoutPriority(rawValue: 200) + containerBottomAnchor.isActive = true + + let colorLablesBottomAnchor = container.bottomAnchor.constraint(equalTo: colorLablesStack.bottomAnchor) + colorLablesBottomAnchor.priority = UILayoutPriority(rawValue: 204) + colorLablesBottomAnchor.isActive = true + + let colorLablesTopAnchor = colorLablesStack.topAnchor.constraint(equalTo: doughnutChart.topAnchor) + colorLablesTopAnchor.priority = UILayoutPriority(rawValue: 204) + colorLablesTopAnchor.isActive = true + + colorLablesStack.topAnchor.constraint(greaterThanOrEqualTo: doughnutChart.topAnchor).isActive = true + container.bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true + container.trailingAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.trailingAnchor).isActive = true + + let centerY = colorLablesStack.centerYAnchor.constraint(equalTo: doughnutChart.centerYAnchor) + centerY.priority = .defaultLow + centerY.isActive = true + + colorLablesStack.leadingAnchor.constraint(equalTo: doughnutChart.trailingAnchor, constant: PaddingThree).isActive = true + colorLablesStack.backgroundColor = UIColor.clear + + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + doughnutChart.updateView(size) + colorLablesStack.updateView(size) + } + + open override func reset() { + super.reset() + colorLablesStack.reset() + } + + open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + doughnutChart.clearLayers() + colorLablesStack.removeAllItemViews() + doughnutChart.setWithModel(model, delegateObject, additionalData) + colorLablesStack.setWithModel(model, delegateObject, additionalData) + } + + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + if model == nil { + let data = try! JSONSerialization.data(withJSONObject: json!) + let decoder = JSONDecoder() + let model = try! decoder.decode(DoughnutChartModel.self, from: data) + setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) + } else { + setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) + } + } + +} + +class ColorViewLabelsStack: MoleculeStackView { + + var dougnutChartModel: DoughnutChartModel? + + override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + let previousModel = self.dougnutChartModel + removeAllItemViews() + guard let dougnutChartModel = model as? DoughnutChartModel else { + return + } + var items: [StackItem]? + if previousModel?.sections.count == dougnutChartModel.sections.count { + items = stackItems + } + stackItems = [] + self.model = MoleculeStackModel(molecules: []) + for (index, chartItemModel) in dougnutChartModel.sections.enumerated() { + let colorViewWithLabel = items?[index].view as? ColorViewWithLabel ?? ColorViewWithLabel() + colorViewWithLabel.setWithModel(chartItemModel, delegateObject, additionalData) + addView(colorViewWithLabel, lastItem: index == dougnutChartModel.sections.count - 1) + } + self.dougnutChartModel = dougnutChartModel + restack() + } +} + +class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { + + var label = Label.commonLabelB2(true) + var colorView = MVMCoreUICommonViewsUtility.commonView() + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 8)! + var heightConstraint: NSLayoutConstraint? + + override func setupView() { + super.setupView() + guard colorView.superview == nil else { + return + } + translatesAutoresizingMaskIntoConstraints = false + addSubview(colorView) + addSubview(label) + + heightConstraint = colorView.heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth()) + heightConstraint?.isActive = true + colorView.widthAnchor.constraint(equalTo: colorView.heightAnchor).isActive = true + colorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + colorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + label.leadingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: PaddingOne).isActive = true + label.topAnchor.constraint(equalTo: topAnchor).isActive = true + trailingAnchor.constraint(greaterThanOrEqualTo: label.trailingAnchor).isActive = true + bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true + + } + + override func updateView(_ size: CGFloat) { + super.updateView(size) + label.updateView(size) + heightConstraint?.constant = sizeObject.getValueBased(onSize: size) + setNeedsDisplay() + } + + override func reset() { + super.reset() + label.reset() + } + + func setAsMolecule() { + label.setAsMolecule() + } + + override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + guard let chartItemModel = model as? ChartItemModel else { + return + } + label.setWithModel(chartItemModel.label, delegateObject, additionalData) + colorView.backgroundColor = UIColor.mfGet(forHex: chartItemModel.color) + } + + override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + Label.setUILabel(label, withJSON: json?.optionalDictionaryForKey("label"), delegate: delegateObject, additionalData: additionalData) + colorView.backgroundColor = UIColor.mfGet(forHex: json?.optionalStringForKey("color") ?? "#000000") + } + +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index c656ab2f..4b597288 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -68,7 +68,8 @@ @"dropDownListItem": DropDownFilterTableViewCell.class, @"headlineBodyButton": HeadlineBodyButton.class, @"eyebrowHeadlineBodyLink": EyebrowHeadlineBodyLink.class, - @"stackItem": StackItem.class + @"stackItem": StackItem.class, + @"doughnutChart": DoughnutChartView.class } mutableCopy]; }); return mapping; diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index ac96c44f..651d18a7 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -29,5 +29,6 @@ import Foundation ModelRegistry.register(LabelAttributeUnderlineModel.self) ModelRegistry.register(LabelAttributeStrikeThroughModel.self) ModelRegistry.register(LabelAttributeActionModel.self) + ModelRegistry.register(DoughnutChartModel.self) } } From be094b3cf91879c7d654d3b25c97ce585e3cb2e0 Mon Sep 17 00:00:00 2001 From: "Murugan, Vimal" Date: Mon, 13 Jan 2020 22:49:51 +0530 Subject: [PATCH 02/19] review comments updated --- .../Models/Molecules/DoughnutChartModel.swift | 61 +------------------ MVMCoreUI/Molecules/DoughnutChart.swift | 21 ++++--- MVMCoreUI/Molecules/DoughnutChartView.swift | 5 -- 3 files changed, 14 insertions(+), 73 deletions(-) diff --git a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift index f6a1efb8..15ecab85 100644 --- a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift +++ b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift @@ -9,50 +9,13 @@ import Foundation @objcMembers public class DoughnutChartModel: MoleculeProtocol { - public var backgroundColor: String? public static var identifier: String = "doughnutChart" public var title: LabelModel? public var subtitle: LabelModel? public var sections: [ChartItemModel] - public var lineWidth: CGFloat = 30.0 - public var lineGap: CGFloat = 0.05 //For 3px gap - public var spaceRequired = true - - enum CodingKeys: String, CodingKey { - case list - case title - case subtitle - case sections - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.title = try typeContainer.decodeMoleculeIfPresent(codingKey: .title) as? LabelModel - self.subtitle = try typeContainer.decodeMoleculeIfPresent(codingKey: .subtitle) as? LabelModel - //self.sections = try typeContainer.decodeMolecules(codingKey: .sections) as! [ChartItemModel] - - //TODO: Hasve to check with scott - self.sections = [] - guard var container = try? typeContainer.nestedUnkeyedContainer(forKey: .sections) else { - return - } - var sections = [ChartItemModel]() - while !container.isAtEnd { - if let element = try container - .decodeIfPresent(ChartItemModel.self) { - sections.append(element) - } - } - self.sections = sections - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeModelIfPresent(title, forKey: .title) - try container.encodeModelIfPresent(subtitle, forKey: .subtitle) - try container.encodeModels(sections as [Model], forKey: .sections) - } + public var lineWidth: CGFloat? + public var spaceRequired: Bool? } @@ -62,24 +25,4 @@ import Foundation public var percent: CGFloat public var color: String public static var identifier: String = "chartItem" - - enum CodingKeys: String, CodingKey { - case label - case percent - case color - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.label = try typeContainer.decodeMolecule(codingKey: .label) as! LabelModel - self.percent = try typeContainer.decode(CGFloat.self, forKey: .percent) - self.color = try typeContainer.decode(String.self, forKey: .color) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeModel(label, forKey: .label) - try container.encode(percent, forKey: .percent) - try container.encode(color, forKey: .color) - } } diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index 08bdefb1..ac19af7d 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -21,7 +21,6 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { var heightConstraint: NSLayoutConstraint? private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! - //private var lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 30)! var height: CGFloat = 150 { didSet { @@ -105,7 +104,6 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { subTitleLabel.setWithModel(doughnutChartModel.subtitle, delegateObject, additionalData) titleLabel.textAlignment = .center subTitleLabel.textAlignment = .center - //lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: doughnutChartModel.lineWidth)! updateLabelContainer() drawGraph() } @@ -118,20 +116,17 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { height: widthHeight) if let doughnutChart = doughnutChartModel { var prevPercent: CGFloat = 0.0 - //If Single item in array. We don't need gap - doughnutChartModel?.lineGap = (doughnutChart.sections.count == 1) ? 0.0 : doughnutChart.lineGap for model in doughnutChart.sections { - prevPercent += drawBar(model, prevPercent, doughnutChart.spaceRequired, doughnutChart.lineGap) + prevPercent += drawBar(model, prevPercent) } } } - func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat, _ spaceRequired: Bool, _ lineGap: CGFloat) -> CGFloat { + func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat) -> CGFloat { let shapeLayer = CAShapeLayer() shapeLayer.frame = containerLayer.frame shapeLayer.lineWidth = lineWidth() - //lineSizeObject.getValueBasedOnApplicationWidth() shapeLayer.fillColor = nil shapeLayer.strokeColor = UIColor.mfGet(forHex: chartModel.color).cgColor @@ -141,7 +136,7 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { let clockwise = true let value: CGFloat = chartModel.percent - let gap: CGFloat = spaceRequired ? lineGap : 0.0 + let gap: CGFloat = spaceReuired() ? lineGap() : 0.0 let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap let circlePath = UIBezierPath(arcCenter: arcCenter, @@ -167,7 +162,15 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { func lineWidth() -> CGFloat { return doughnutChartModel?.lineWidth ?? 30 - //lineSizeObject.getValueBasedOnApplicationWidth() + 5 + } + + func lineGap() -> CGFloat { + //If array is having the single item then no space required + return doughnutChartModel?.sections.count == 1 ? 0.0 : 0.05 + } + + func spaceReuired() -> Bool { + return doughnutChartModel?.spaceRequired ?? true } func updateLabelContainer() { diff --git a/MVMCoreUI/Molecules/DoughnutChartView.swift b/MVMCoreUI/Molecules/DoughnutChartView.swift index ebe77200..95e23440 100644 --- a/MVMCoreUI/Molecules/DoughnutChartView.swift +++ b/MVMCoreUI/Molecules/DoughnutChartView.swift @@ -171,9 +171,4 @@ class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { colorView.backgroundColor = UIColor.mfGet(forHex: chartItemModel.color) } - override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - Label.setUILabel(label, withJSON: json?.optionalDictionaryForKey("label"), delegate: delegateObject, additionalData: additionalData) - colorView.backgroundColor = UIColor.mfGet(forHex: json?.optionalStringForKey("color") ?? "#000000") - } - } From 9329f0ca15cfd8dac6a1c5979f7b8e36fbf348b2 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 15 Jan 2020 13:28:40 -0500 Subject: [PATCH 03/19] update left and right padding --- MVMCoreUI/Molecules/DoughnutChart.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index d7f25ac6..d58dc7c4 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -173,10 +173,15 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { return doughnutChartModel?.spaceRequired ?? true } - func updateLabelContainer() { - labelContainer.leftPin?.constant = lineWidth() - labelContainer.topPin?.constant = lineWidth() - labelContainer.setNeedsDisplay() - } + func updateLabelContainer() { + labelContainer.layoutIfNeeded() + let radius = sizeObject.getValueBasedOnApplicationWidth()/2 - lineWidth() + let labelheight = labelContainer.frame.height/2 + let padding = sizeObject.getValueBasedOnApplicationWidth()/2 - sqrt(pow(radius, 2) - pow(labelheight, 2)) + + labelContainer.leftPin?.constant = padding + labelContainer.topPin?.constant = padding + labelContainer.setNeedsDisplay() + } } From 00d78cfdedcdf238466c35c6fea4852cac6da925 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 15 Jan 2020 13:53:54 -0500 Subject: [PATCH 04/19] update label frame --- MVMCoreUI/Molecules/DoughnutChart.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index d58dc7c4..e6a169f1 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -20,9 +20,9 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { var labelContainer = ViewConstrainingView.empty() var heightConstraint: NSLayoutConstraint? - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 100)! - var height: CGFloat = 150 { + var height: CGFloat = 100 { didSet { if height != oldValue { sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! @@ -174,14 +174,14 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } func updateLabelContainer() { + labelContainer.setNeedsDisplay() labelContainer.layoutIfNeeded() let radius = sizeObject.getValueBasedOnApplicationWidth()/2 - lineWidth() let labelheight = labelContainer.frame.height/2 let padding = sizeObject.getValueBasedOnApplicationWidth()/2 - sqrt(pow(radius, 2) - pow(labelheight, 2)) labelContainer.leftPin?.constant = padding - labelContainer.topPin?.constant = padding - labelContainer.setNeedsDisplay() + labelContainer.topPin?.constant = max(radius - labelheight, labelheight) } } From e5accc7611d5f836e2793c9e3205ca536822b574 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 15 Jan 2020 13:54:31 -0500 Subject: [PATCH 05/19] update height --- MVMCoreUI/Molecules/DoughnutChart.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index e6a169f1..1175a563 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -20,9 +20,9 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { var labelContainer = ViewConstrainingView.empty() var heightConstraint: NSLayoutConstraint? - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 100)! + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! - var height: CGFloat = 100 { + var height: CGFloat = 150 { didSet { if height != oldValue { sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! From de27398c49de13fac16143703c90698db43fb865 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 15 Jan 2020 18:59:48 -0500 Subject: [PATCH 06/19] leftRightLabelView --- MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift index 5e091446..be703bef 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift @@ -9,7 +9,7 @@ import UIKit @objcMembers public class LeftRightLabelModel: MoleculeProtocol { - public static var identifier: String = "leftRightLabel" + public static var identifier: String = "leftRightLabelView" public var backgroundColor: Color? public var leftText: LabelModel public var rightText: LabelModel From 9bf9d711951dc078eab2b1268f2ae28e2f5fe2b1 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 13:30:30 -0500 Subject: [PATCH 07/19] optional --- MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift | 2 +- .../VerticalCombinationViews/HeadlineBodyModel.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift index be703bef..73abb4d6 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift @@ -12,5 +12,5 @@ import UIKit public static var identifier: String = "leftRightLabelView" public var backgroundColor: Color? public var leftText: LabelModel - public var rightText: LabelModel + public var rightText: LabelModel? } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift index 6ce51d3e..bc63c12c 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift @@ -10,8 +10,8 @@ import Foundation @objcMembers public class HeadlineBodyModel: MoleculeProtocol { public static var identifier: String = "headlineBody" - public var headline: LabelModel - public var body: LabelModel + public var headline: LabelModel? + public var body: LabelModel? public var style: String? public var backgroundColor: Color? From 71efaa9026dd839c0af64a1c3e8220e4c4907354 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 13:51:55 -0500 Subject: [PATCH 08/19] alignment --- MVMCoreUI/Organisms/MoleculeStackView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 9f2f526f..aad48bd0 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -107,8 +107,6 @@ open class MoleculeStackView: Container { } restack() - stackModel?.useHorizontalMargins = moleculesShouldSetHorizontalMargins - stackModel?.useVerticalMargins = moleculesShouldSetVerticalMargins } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { From db0d9f43531d19da30b7991a081e738133735d51 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 14:59:24 -0500 Subject: [PATCH 09/19] Container protocol Doughnut chart model Break stack into regular and dynamic components --- MVMCoreUI.xcodeproj/project.pbxproj | 65 +++-- MVMCoreUI/Containers/Container.swift | 15 +- MVMCoreUI/Containers/ContainerProtocol.swift | 15 ++ .../{ => Doughnut}/DoughnutChart.swift | 80 +++--- .../Doughnut}/DoughnutChartModel.swift | 22 +- .../{ => Doughnut}/DoughnutChartView.swift | 103 ++++--- .../Molecules/Items/MoleculeStackItem.swift | 16 ++ .../Items/MoleculeStackItemModel.swift | 45 ++++ MVMCoreUI/Molecules/Items/StackItem.swift | 8 +- .../Molecules/Items/StackItemModel.swift | 39 +-- .../Items/StackItemModelProtocol.swift | 15 ++ .../EyebrowHeadlineBodyLink.swift | 25 +- MVMCoreUI/Organisms/MoleculeStackModel.swift | 10 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 252 +----------------- MVMCoreUI/Organisms/Stack.swift | 239 +++++++++++++++++ MVMCoreUI/Organisms/StackModel.swift | 22 ++ MVMCoreUI/Organisms/StackModelProtocol.swift | 18 ++ .../MVMCoreUIMoleculeMappingObject.m | 1 - .../OtherHandlers/MoleculeObjectMapping.swift | 4 +- .../Templates/MoleculeStackTemplate.swift | 4 +- .../NSLayoutConstraintAxis+Extension.swift | 31 +++ 21 files changed, 602 insertions(+), 427 deletions(-) create mode 100644 MVMCoreUI/Containers/ContainerProtocol.swift rename MVMCoreUI/Molecules/{ => Doughnut}/DoughnutChart.swift (65%) rename MVMCoreUI/{Models/Molecules => Molecules/Doughnut}/DoughnutChartModel.swift (50%) rename MVMCoreUI/Molecules/{ => Doughnut}/DoughnutChartView.swift (55%) create mode 100644 MVMCoreUI/Molecules/Items/MoleculeStackItem.swift create mode 100644 MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift create mode 100644 MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift create mode 100644 MVMCoreUI/Organisms/Stack.swift create mode 100644 MVMCoreUI/Organisms/StackModel.swift create mode 100644 MVMCoreUI/Organisms/StackModelProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 32d9b7ba..e2e09a72 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -105,10 +105,10 @@ 94C661D923CCF4B400D9FE5B /* LeftRightLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9402C34F23A2CEA3004B974C /* LeftRightLabelModel.swift */; }; 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B33239813C50067DD0F /* UIColor+Extension.swift */; }; C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; }; - C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */; }; C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; }; C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69523C990BC00BFB94E /* DoughnutChart.swift */; }; C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69723C990C200BFB94E /* DoughnutChartView.swift */; }; + C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */; }; @@ -128,10 +128,17 @@ D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; D260105323CEA61600764D80 /* ToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105223CEA61600764D80 /* ToggleModel.swift */; }; D260105523CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */; }; + D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105823D0A92900764D80 /* ContainerProtocol.swift */; }; + D260105B23D0BB7100764D80 /* StackModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105A23D0BB7100764D80 /* StackModelProtocol.swift */; }; + D260105D23D0BCD400764D80 /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105C23D0BCD400764D80 /* Stack.swift */; }; + D260105F23D0BFFC00764D80 /* StackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105E23D0BFFC00764D80 /* StackItem.swift */; }; + D260106123D0C02A00764D80 /* StackItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106023D0C02A00764D80 /* StackItemModelProtocol.swift */; }; + D260106323D0C05000764D80 /* StackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106223D0C05000764D80 /* StackItemModel.swift */; }; + D260106523D0CEA700764D80 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; }; D260D7B122D65BDD007E7233 /* MVMCoreUIPageControl.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */; }; D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D268C70C2386DFFD007F2C1C /* StackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* StackItemModel.swift */; }; + D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */; }; D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */; }; D268C712238D6699007F2C1C /* DropDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C711238D6699007F2C1C /* DropDown.swift */; }; D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* StandardFooterView.swift */; }; @@ -282,7 +289,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 */; }; + D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.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 */; }; @@ -338,7 +345,7 @@ 01EB3683236097C0006832FA /* MoleculeProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeProtocol.swift; sourceTree = ""; }; 01EB368823609801006832FA /* LabelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelModel.swift; sourceTree = ""; }; 01EB368923609801006832FA /* ListItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListItemModel.swift; sourceTree = ""; }; - 01EB368A23609801006832FA /* StackItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackItemModel.swift; sourceTree = ""; }; + 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackItemModel.swift; sourceTree = ""; }; 01EB368B23609801006832FA /* MoleculeStackModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackModel.swift; sourceTree = ""; }; 01EB368C23609801006832FA /* HeaderModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderModel.swift; sourceTree = ""; }; 01EB368D23609801006832FA /* HeadlineBodyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyModel.swift; sourceTree = ""; }; @@ -380,10 +387,10 @@ 94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeImageModel.swift; sourceTree = ""; }; 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeActionModel.swift; sourceTree = ""; }; C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; - C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadLineBodyCaretLinkImage.swift; sourceTree = ""; }; C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = ""; }; C695A69523C990BC00BFB94E /* DoughnutChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChart.swift; sourceTree = ""; }; C695A69723C990C200BFB94E /* DoughnutChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartView.swift; sourceTree = ""; }; + C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadLineBodyCaretLinkImage.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraintAxis+Extension.swift"; sourceTree = ""; }; @@ -403,6 +410,13 @@ D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; D260105223CEA61600764D80 /* ToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleModel.swift; sourceTree = ""; }; D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUISwitch+Model.swift"; sourceTree = ""; }; + D260105823D0A92900764D80 /* ContainerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerProtocol.swift; sourceTree = ""; }; + D260105A23D0BB7100764D80 /* StackModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackModelProtocol.swift; sourceTree = ""; }; + D260105C23D0BCD400764D80 /* Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; + D260105E23D0BFFC00764D80 /* StackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItem.swift; sourceTree = ""; }; + D260106023D0C02A00764D80 /* StackItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItemModelProtocol.swift; sourceTree = ""; }; + D260106223D0C05000764D80 /* StackItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItemModel.swift; sourceTree = ""; }; + D260106423D0CEA700764D80 /* StackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackModel.swift; sourceTree = ""; }; D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPageControl.h; sourceTree = ""; }; D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIPageControl.m; sourceTree = ""; }; D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = ""; }; @@ -572,7 +586,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 = ""; }; + D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.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 = ""; }; @@ -665,10 +679,6 @@ 01EB368723609801006832FA /* Molecules */ = { isa = PBXGroup; children = ( - C695A69323C9909000BFB94E /* DoughnutChartModel.swift */, - 01EB368923609801006832FA /* ListItemModel.swift */, - 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */, - 01EB368B23609801006832FA /* MoleculeStackModel.swift */, 011B58F323A2CCC80085F53C /* DropDownModel.swift */, 01EB368C23609801006832FA /* HeaderModel.swift */, 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, @@ -806,6 +816,9 @@ D22479902316A9CB003FCCF9 /* Organisms */ = { isa = PBXGroup; children = ( + D260105A23D0BB7100764D80 /* StackModelProtocol.swift */, + D260106423D0CEA700764D80 /* StackModel.swift */, + D260105C23D0BCD400764D80 /* Stack.swift */, 01EB368B23609801006832FA /* MoleculeStackModel.swift */, D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */, D2A6390022CBB1820052ED1F /* Carousel.swift */, @@ -826,8 +839,11 @@ D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */, 011B58F123A2AE2C0085F53C /* DropDownListItemModel.swift */, D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */, - 01EB368A23609801006832FA /* StackItemModel.swift */, - D2FB151C23A40F1500C20E10 /* StackItem.swift */, + D260106023D0C02A00764D80 /* StackItemModelProtocol.swift */, + D260106223D0C05000764D80 /* StackItemModel.swift */, + D260105E23D0BFFC00764D80 /* StackItem.swift */, + 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */, + D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */, ); path = Items; sourceTree = ""; @@ -841,6 +857,16 @@ path = Legacy; sourceTree = ""; }; + D260105723CF9CC500764D80 /* Doughnut */ = { + isa = PBXGroup; + children = ( + C695A69323C9909000BFB94E /* DoughnutChartModel.swift */, + C695A69523C990BC00BFB94E /* DoughnutChart.swift */, + C695A69723C990C200BFB94E /* DoughnutChartView.swift */, + ); + path = Doughnut; + sourceTree = ""; + }; D29DF0C221E404D4003B2FB9 = { isa = PBXGroup; children = ( @@ -937,8 +963,7 @@ D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */, - C695A69523C990BC00BFB94E /* DoughnutChart.swift */, - C695A69723C990C200BFB94E /* DoughnutChartView.swift */, + D260105723CF9CC500764D80 /* Doughnut */, ); path = Molecules; sourceTree = ""; @@ -986,6 +1011,7 @@ D29DF2B721E7BE79003B2FB9 /* TabBarController */, D29DF2B621E7BE66003B2FB9 /* SplitViewController */, D2B18B93236214AD00A9AEDC /* NavigationController.swift */, + D260105823D0A92900764D80 /* ContainerProtocol.swift */, D243859823A16B1800332775 /* Container.swift */, ); path = Containers; @@ -1424,7 +1450,7 @@ 94C2D9A923872E5E0006CF46 /* LabelAttributeImageModel.swift in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, - D2FB151D23A40F1500C20E10 /* StackItem.swift in Sources */, + D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */, 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */, @@ -1449,6 +1475,7 @@ D29DF2C521E7BF57003B2FB9 /* MFTabBarSwipeAnimator.m in Sources */, 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, + D260106323D0C05000764D80 /* StackItemModel.swift in Sources */, 01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */, 0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */, D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */, @@ -1465,6 +1492,7 @@ D28A838723CCCF6500DFE4FC /* MFTextButton+ModelExtension.swift in Sources */, C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */, 014AA72D23C5059B006F3E93 /* StackPageTemplateModel.swift in Sources */, + D260106123D0C02A00764D80 /* StackItemModelProtocol.swift in Sources */, 012A88C4238D86E600FE3DA1 /* CollectionCellMoleculeProtocol.swift in Sources */, 94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */, 014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */, @@ -1486,7 +1514,7 @@ D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */, 01EB369023609801006832FA /* ListItemModel.swift in Sources */, D28A838323CCBD3F00DFE4FC /* CircleProgressModel.swift in Sources */, - D268C70C2386DFFD007F2C1C /* StackItemModel.swift in Sources */, + D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */, DBEFFA04225A829700230692 /* Label.swift in Sources */, D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, 01509D952327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift in Sources */, @@ -1504,6 +1532,7 @@ D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, + D260105D23D0BCD400764D80 /* Stack.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 01EB368F23609801006832FA /* LabelModel.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, @@ -1559,9 +1588,11 @@ D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, D243859923A16B1800332775 /* Container.swift in Sources */, + D260105B23D0BB7100764D80 /* StackModelProtocol.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */, D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */, + D260105F23D0BFFC00764D80 /* StackItem.swift in Sources */, 01EB369323609801006832FA /* HeaderModel.swift in Sources */, D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, 0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */, @@ -1601,6 +1632,7 @@ 014AA72E23C5059B006F3E93 /* StackCenteredPageTemplateModel.swift in Sources */, 0ABD136B237B193A0081388D /* EntryFieldContainer.swift in Sources */, D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */, + D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */, C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, @@ -1616,6 +1648,7 @@ D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */, 946EE1BA237B66D80036751F /* MoleculeModelHelper.swift in Sources */, 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */, + D260106523D0CEA700764D80 /* StackModel.swift in Sources */, D29770F521F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/MVMCoreUI/Containers/Container.swift b/MVMCoreUI/Containers/Container.swift index ab495b81..43fd62c8 100644 --- a/MVMCoreUI/Containers/Container.swift +++ b/MVMCoreUI/Containers/Container.swift @@ -196,7 +196,7 @@ public class ContainerHelper: NSObject { } } -open class Container: View { +open class Container: View, ContainerProtocol { var view: UIView? let containerHelper = ContainerHelper() var containerModel: ContainerModelProtocol? { @@ -208,6 +208,19 @@ open class Container: View { guard let containerModel = model as? ContainerModelProtocol else { return } containerHelper.set(with: containerModel, for: view as? MVMCoreUIViewConstrainingProtocol) } + + // MARK:- ContainerProtocol + public func alignHorizontal(_ alignment: UIStackView.Alignment) { + containerHelper.alignHorizontal(alignment) + } + + public func alignVertical(_ alignment: UIStackView.Alignment) { + containerHelper.alignVertical(alignment) + } + + public func constrainView(_ view: UIView) { + containerHelper.constrainView(view) + } } // MARK: - MVMCoreViewProtocol diff --git a/MVMCoreUI/Containers/ContainerProtocol.swift b/MVMCoreUI/Containers/ContainerProtocol.swift new file mode 100644 index 00000000..8079ca24 --- /dev/null +++ b/MVMCoreUI/Containers/ContainerProtocol.swift @@ -0,0 +1,15 @@ +// +// ContainerProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol ContainerProtocol { + func alignHorizontal(_ alignment: UIStackView.Alignment) + func alignVertical(_ alignment: UIStackView.Alignment) + func constrainView(_ view: UIView) +} diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift similarity index 65% rename from MVMCoreUI/Molecules/DoughnutChart.swift rename to MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index d7f25ac6..5b0763e6 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -8,21 +8,23 @@ import UIKit -open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { - - var containerView = MVMCoreUICommonViewsUtility.commonView() - var containerLayer = CALayer() +open class DoughnutChart: View { + var doughnutLayer = CALayer() var titleLabel = Label.commonLabelH3(true) var subTitleLabel = Label.commonLabelB2(true) var doughnutChartModel: DoughnutChartModel? { get { return model as? DoughnutChartModel } } - var labelContainer = ViewConstrainingView.empty() + var labelContainer = MVMCoreUICommonViewsUtility.commonView() + var labelContainerLeftConstraint: NSLayoutConstraint? + var labelContainerTopConstraint: NSLayoutConstraint? + var labelContainerBottomConstraint: NSLayoutConstraint? + var labelContainerRightConstraint: NSLayoutConstraint? var heightConstraint: NSLayoutConstraint? - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! - - var height: CGFloat = 150 { + static let heightConstant: CGFloat = 150 + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: heightConstant)! + var height: CGFloat = heightConstant { didSet { if height != oldValue { sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! @@ -47,17 +49,16 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { clearLayers() } - public override func setAsMolecule() { + public override func setAsMolecule() { titleLabel.setAsMolecule() subTitleLabel.setAsMolecule() } open override func setupView() { super.setupView() - guard containerView.superview == nil else { + guard labelContainer.superview == nil else { return } - addSubview(containerView) addSubview(labelContainer) labelContainer.addSubview(titleLabel) @@ -68,36 +69,35 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { //Make label font size to adjust width if label content is high titleLabel.numberOfLines = 1 titleLabel.adjustsFontSizeToFitWidth = true - - containerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - containerView.topAnchor.constraint(equalTo: topAnchor).isActive = true - bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true - trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true - containerView.layer.addSublayer(containerLayer) - heightConstraint = containerView.heightAnchor.constraint(equalToConstant: + + layer.addSublayer(doughnutLayer) + heightConstraint = heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth()) heightConstraint?.isActive = true - containerView.widthAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true + widthAnchor.constraint(equalTo: heightAnchor).isActive = true - labelContainer.leftPin = labelContainer.leftAnchor.constraint(greaterThanOrEqualTo: containerView.leftAnchor, constant: lineWidth()) - labelContainer.leftPin?.isActive = true - labelContainer.topPin = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: lineWidth()) - labelContainer.topPin?.isActive = true - labelContainer.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true - labelContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true + labelContainerLeftConstraint = labelContainer.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor, constant: lineWidth()) + labelContainerLeftConstraint?.isActive = true + labelContainerTopConstraint = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: lineWidth()) + labelContainerTopConstraint?.isActive = true + labelContainerRightConstraint = rightAnchor.constraint(greaterThanOrEqualTo: labelContainer.rightAnchor, constant: lineWidth()) + labelContainerRightConstraint?.isActive = true + labelContainerBottomConstraint = bottomAnchor.constraint(greaterThanOrEqualTo: labelContainer.bottomAnchor, constant: lineWidth()) + labelContainerBottomConstraint?.isActive = true + labelContainer.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + labelContainer.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true) NSLayoutConstraint.constraintPinSubview(subTitleLabel, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true) _ = NSLayoutConstraint(pinFirstView: titleLabel, toSecondView: subTitleLabel, withConstant: 0, directionVertical: true) //Rotate view for initial draw - containerLayer.transform = CATransform3DMakeRotation(1 * .pi, 0.0, 0.0, 1.0) - + doughnutLayer.transform = CATransform3DMakeRotation(1 * .pi, 0.0, 0.0, 1.0) } open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.setWithModel(model, delegateObject, additionalData) clearLayers() - guard let doughnutChartModel = model as? DoughnutChartModel else { + guard let doughnutChartModel = doughnutChartModel else { return } titleLabel.setWithModel(doughnutChartModel.title, delegateObject, additionalData) @@ -111,7 +111,7 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { func drawGraph() { clearLayers() let widthHeight = sizeObject.getValueBasedOnApplicationWidth() - containerLayer.frame = CGRect(x: 0, y: 0, + doughnutLayer.frame = CGRect(x: 0, y: 0, width: widthHeight, height: widthHeight) if let doughnutChart = doughnutChartModel { @@ -122,17 +122,16 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } } - func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat) -> CGFloat { + func drawBar(_ chartModel: DoughnutChartItemModel, _ prevPercent: CGFloat) -> CGFloat { let shapeLayer = CAShapeLayer() - shapeLayer.frame = containerLayer.frame + shapeLayer.frame = doughnutLayer.frame shapeLayer.lineWidth = lineWidth() shapeLayer.fillColor = nil - shapeLayer.strokeColor = UIColor.mfGet(forHex: chartModel.color).cgColor + shapeLayer.strokeColor = chartModel.color.uiColor.cgColor let arcCenter = shapeLayer.position - let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 - //lineSizeObject.getValueBasedOnApplicationWidth() / 2.0 + let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 let clockwise = true let value: CGFloat = chartModel.percent @@ -146,12 +145,12 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { clockwise: clockwise) shapeLayer.path = circlePath.cgPath - containerLayer.addSublayer(shapeLayer) + doughnutLayer.addSublayer(shapeLayer) return value } func clearLayers() { - containerLayer.sublayers?.forEach({ $0.removeFromSuperlayer() }) + doughnutLayer.sublayers?.forEach({ $0.removeFromSuperlayer() }) } func updateContainer() { @@ -161,7 +160,7 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } func lineWidth() -> CGFloat { - return doughnutChartModel?.lineWidth ?? 30 + return 30 } func lineGap() -> CGFloat { @@ -174,9 +173,10 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } func updateLabelContainer() { - labelContainer.leftPin?.constant = lineWidth() - labelContainer.topPin?.constant = lineWidth() + labelContainerLeftConstraint?.constant = lineWidth() + labelContainerTopConstraint?.constant = lineWidth() + labelContainerRightConstraint?.constant = lineWidth() + labelContainerBottomConstraint?.constant = lineWidth() labelContainer.setNeedsDisplay() } - } diff --git a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift similarity index 50% rename from MVMCoreUI/Models/Molecules/DoughnutChartModel.swift rename to MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift index 7aab35fd..1f4098e2 100644 --- a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift @@ -13,16 +13,24 @@ import Foundation public static var identifier: String = "doughnutChart" public var title: LabelModel? public var subtitle: LabelModel? - public var sections: [ChartItemModel] - public var lineWidth: CGFloat? + public var sections: [DoughnutChartItemModel] public var spaceRequired: Bool? + + public init(sections: [DoughnutChartItemModel]) { + self.sections = sections + } } - -@objcMembers public class ChartItemModel: MoleculeProtocol { +@objcMembers public class DoughnutChartItemModel: MoleculeProtocol { public var backgroundColor: Color? + public static var identifier: String = "doughnutChartItem" public var label: LabelModel - public var percent: CGFloat - public var color: String - public static var identifier: String = "chartItem" + @Percent public var percent: CGFloat + public var color: Color + + public init(percent: CGFloat, color: Color, label: LabelModel) { + self.percent = percent + self.color = color + self.label = label + } } diff --git a/MVMCoreUI/Molecules/DoughnutChartView.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift similarity index 55% rename from MVMCoreUI/Molecules/DoughnutChartView.swift rename to MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift index a54c04d4..4893edbc 100644 --- a/MVMCoreUI/Molecules/DoughnutChartView.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift @@ -9,54 +9,48 @@ import Foundation @objcMembers open class DoughnutChartView: View { - var doughnutChart = DoughnutChart(frame: CGRect.zero) var colorLablesStack = ColorViewLabelsStack() - var container = MVMCoreUICommonViewsUtility.commonView() var doughnutChartModel: DoughnutChartModel? { get { return model as? DoughnutChartModel } } open override func setupView() { super.setupView() - guard container.superview == nil else { + guard doughnutChart.superview == nil else { return } - - addSubview(container) doughnutChart.translatesAutoresizingMaskIntoConstraints = false - container.addSubview(doughnutChart) + addSubview(doughnutChart) colorLablesStack.translatesAutoresizingMaskIntoConstraints = false - container.addSubview(colorLablesStack) + addSubview(colorLablesStack) - NSLayoutConstraint.constraintPinSubview(container, pinTop: true, pinBottom: true, pinLeft: true, pinRight: true) - doughnutChart.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true - doughnutChart.topAnchor.constraint(equalTo: container.topAnchor, constant: PaddingFour).isActive = true - container.bottomAnchor.constraint(greaterThanOrEqualTo: doughnutChart.bottomAnchor).isActive = true + doughnutChart.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + doughnutChart.topAnchor.constraint(equalTo: topAnchor, constant: PaddingFour).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: doughnutChart.bottomAnchor).isActive = true - let containerBottomAnchor = container.bottomAnchor.constraint(equalTo: doughnutChart.bottomAnchor) - containerBottomAnchor.priority = UILayoutPriority(rawValue: 200) - containerBottomAnchor.isActive = true + let doughnutBottomAnchor = bottomAnchor.constraint(equalTo: doughnutChart.bottomAnchor) + doughnutBottomAnchor.priority = UILayoutPriority(rawValue: 200) + doughnutBottomAnchor.isActive = true - let colorLablesBottomAnchor = container.bottomAnchor.constraint(equalTo: colorLablesStack.bottomAnchor) + let colorLablesBottomAnchor = bottomAnchor.constraint(equalTo: colorLablesStack.bottomAnchor) colorLablesBottomAnchor.priority = UILayoutPriority(rawValue: 204) colorLablesBottomAnchor.isActive = true let colorLablesTopAnchor = colorLablesStack.topAnchor.constraint(equalTo: doughnutChart.topAnchor) - colorLablesTopAnchor.priority = UILayoutPriority(rawValue: 204) + colorLablesTopAnchor.priority = .defaultLow colorLablesTopAnchor.isActive = true colorLablesStack.topAnchor.constraint(greaterThanOrEqualTo: doughnutChart.topAnchor).isActive = true - container.bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true - container.trailingAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.trailingAnchor).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true + trailingAnchor.constraint(equalTo: colorLablesStack.trailingAnchor).isActive = true let centerY = colorLablesStack.centerYAnchor.constraint(equalTo: doughnutChart.centerYAnchor) - centerY.priority = .defaultLow + centerY.priority = UILayoutPriority(rawValue: 500) centerY.isActive = true colorLablesStack.leadingAnchor.constraint(equalTo: doughnutChart.trailingAnchor, constant: PaddingThree).isActive = true colorLablesStack.backgroundColor = UIColor.clear - } open override func updateView(_ size: CGFloat) { @@ -67,56 +61,51 @@ import Foundation open override func reset() { super.reset() + doughnutChart.reset() colorLablesStack.reset() } open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.setWithModel(model, delegateObject, additionalData) - doughnutChart.clearLayers() - colorLablesStack.removeAllItemViews() + + guard let model = doughnutChartModel else { return } doughnutChart.setWithModel(model, delegateObject, additionalData) - colorLablesStack.setWithModel(model, delegateObject, additionalData) + + // Create the stack model + var stackItems: [MoleculeStackItemModel] = [] + for item in model.sections { + stackItems.append(MoleculeStackItemModel(with: item)) + } + let stack = MoleculeStackModel(molecules: stackItems) + stack.verticalAlignment = .fill + colorLablesStack.setWithModel(stack, delegateObject, additionalData) } open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - if model == nil { - let data = try! JSONSerialization.data(withJSONObject: json!) - let decoder = JSONDecoder() - let model = try! decoder.decode(DoughnutChartModel.self, from: data) - setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) - } else { - setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) - } + guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: DoughnutChartModel.self) else { return } + setWithModel(model, delegateObject, additionalData) + } +} + +extension DoughnutChartView: MVMCoreUIViewConstrainingProtocol { + open func horizontalAlignment() -> UIStackView.Alignment { + return .leading } - } class ColorViewLabelsStack: MoleculeStackView { - - var dougnutChartModel: DoughnutChartModel? - override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - let previousModel = self.dougnutChartModel - removeAllItemViews() - guard let dougnutChartModel = model as? DoughnutChartModel else { - return + override func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { + guard let stackItemModels = stackModel?.molecules else { return } + for model in stackItemModels { + let view = ColorViewWithLabel() + let stackItem = MoleculeStackItem(andContain: view) + stackItem.setWithModel(model, delegate, nil) + stackItems.append(stackItem) } - var items: [StackItem]? - if previousModel?.sections.count == dougnutChartModel.sections.count { - items = stackItems - } - stackItems = [] - self.model = MoleculeStackModel(molecules: []) - for (index, chartItemModel) in dougnutChartModel.sections.enumerated() { - let colorViewWithLabel = items?[index].view as? ColorViewWithLabel ?? ColorViewWithLabel() - colorViewWithLabel.setWithModel(chartItemModel, delegateObject, additionalData) - addView(colorViewWithLabel, lastItem: index == dougnutChartModel.sections.count - 1) - } - self.dougnutChartModel = dougnutChartModel - restack() } } -class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { +class ColorViewWithLabel: View { var label = Label.commonLabelB2(true) var colorView = MVMCoreUICommonViewsUtility.commonView() @@ -140,9 +129,8 @@ class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { label.leadingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: PaddingOne).isActive = true label.topAnchor.constraint(equalTo: topAnchor).isActive = true - trailingAnchor.constraint(greaterThanOrEqualTo: label.trailingAnchor).isActive = true + trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true - } override func updateView(_ size: CGFloat) { @@ -163,11 +151,10 @@ class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.setWithModel(model, delegateObject, additionalData) - guard let chartItemModel = model as? ChartItemModel else { + guard let chartItemModel = model as? DoughnutChartItemModel else { return } label.setWithModel(chartItemModel.label, delegateObject, additionalData) - colorView.backgroundColor = UIColor.mfGet(forHex: chartItemModel.color) + colorView.backgroundColor = chartItemModel.color.uiColor } - } diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift new file mode 100644 index 00000000..d883f93e --- /dev/null +++ b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift @@ -0,0 +1,16 @@ +// +// StackItem.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 12/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// +// A list item that contains a molecule + +import UIKit + +open class MoleculeStackItem: MoleculeContainer { + var stackItemModel: StackItemModel? { + get { return model as? StackItemModel } + } +} diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift b/MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift new file mode 100644 index 00000000..e2ee337d --- /dev/null +++ b/MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift @@ -0,0 +1,45 @@ +// +// MoleculeStackItem.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 10/4/19. +// Copyright © 2019 Suresh, Kamlesh. All rights reserved. +// + +import Foundation + +@objcMembers public class MoleculeStackItemModel: MoleculeContainerModel, MoleculeProtocol, StackItemModelProtocol { + public static var identifier: String = "stackItem" + public var backgroundColor: Color? + public var spacing: CGFloat? + public var percent: Int? + public var gone: Bool = false + + enum MoleculeStackItemCodingKeys: String, CodingKey { + case spacing + case percent + case gone + } + + public override init(with moleculeModel: MoleculeProtocol) { + super.init(with: moleculeModel) + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: MoleculeStackItemCodingKeys.self) + spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) + percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) + if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) { + self.gone = gone + } + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: MoleculeStackItemCodingKeys.self) + try container.encodeIfPresent(spacing, forKey: .spacing) + try container.encodeIfPresent(percent, forKey: .percent) + try container.encode(gone, forKey: .gone) + } +} diff --git a/MVMCoreUI/Molecules/Items/StackItem.swift b/MVMCoreUI/Molecules/Items/StackItem.swift index 59c30962..07cae3d0 100644 --- a/MVMCoreUI/Molecules/Items/StackItem.swift +++ b/MVMCoreUI/Molecules/Items/StackItem.swift @@ -2,13 +2,13 @@ // StackItem.swift // MVMCoreUI // -// Created by Scott Pfeil on 12/13/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. // -import UIKit +import Foundation -open class StackItem: MoleculeContainer { +open class StackItem: Container { var stackItemModel: StackItemModel? { get { return model as? StackItemModel } } diff --git a/MVMCoreUI/Molecules/Items/StackItemModel.swift b/MVMCoreUI/Molecules/Items/StackItemModel.swift index d19f2401..9ab86687 100644 --- a/MVMCoreUI/Molecules/Items/StackItemModel.swift +++ b/MVMCoreUI/Molecules/Items/StackItemModel.swift @@ -1,45 +1,20 @@ // -// MoleculeStackItem.swift +// StackItemModel.swift // MVMCoreUI // -// Created by Suresh, Kamlesh on 10/4/19. -// Copyright © 2019 Suresh, Kamlesh. All rights reserved. +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. // import Foundation -@objcMembers public class StackItemModel: MoleculeContainerModel, MoleculeProtocol { - public static var identifier: String = "stackItem" - public var backgroundColor: Color? +@objcMembers public class StackItemModel: StackItemModelProtocol, Codable { public var spacing: CGFloat? public var percent: Int? public var gone: Bool = false - enum MoleculeStackItemCodingKeys: String, CodingKey { - case spacing - case percent - case gone - } - - public override init(with moleculeModel: MoleculeProtocol) { - super.init(with: moleculeModel) - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: MoleculeStackItemCodingKeys.self) - spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) - percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) - if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) { - self.gone = gone - } - try super.init(from: decoder) - } - - public override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: MoleculeStackItemCodingKeys.self) - try container.encodeIfPresent(spacing, forKey: .spacing) - try container.encodeIfPresent(percent, forKey: .percent) - try container.encode(gone, forKey: .gone) + public convenience init(gone: Bool) { + self.init() + self.gone = gone } } diff --git a/MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift b/MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift new file mode 100644 index 00000000..2223ed39 --- /dev/null +++ b/MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift @@ -0,0 +1,15 @@ +// +// StackItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol StackItemModelProtocol { + var spacing: CGFloat? { get set } + var percent: Int? { get set } + var gone: Bool { get set } +} diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index d5557eca..01844e07 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -19,7 +19,7 @@ struct EyebrowHeadlineBodyLinkModel: MoleculeProtocol { } @objcMembers open class EyebrowHeadlineBodyLink: Container { - let stack = MoleculeStackView(frame: .zero) + let stack = Stack(frame: .zero) let eyebrow = Label.commonLabelB3(true) let headline = Label.commonLabelB1(true) let body = Label.commonLabelB2(true) @@ -34,19 +34,7 @@ struct EyebrowHeadlineBodyLinkModel: MoleculeProtocol { guard stack.superview == nil else { return } - let eyebrowStackItem = StackItemModel(with: casteModel!.eyeBrow!) - let headlineStackItem = StackItemModel(with: casteModel!.headline!) - let bodyStackItem = StackItemModel(with: casteModel!.body!) - let linkStackItem = StackItemModel(with: casteModel!.link!) - - // To visually take into account the extra padding in the intrinsic content of a button. - linkStackItem.spacing = -6 - - let stackModel = MoleculeStackModel(molecules: [eyebrowStackItem,headlineStackItem,bodyStackItem,linkStackItem]) - stackModel.spacing = 0 - stack.model = stackModel stack.stackItems = [StackItem(andContain: eyebrow),StackItem(andContain: headline),StackItem(andContain: body),StackItem(andContain: link)] - addSubview(stack) NSLayoutConstraint.constraintPinSubview(toSuperview: stack) } @@ -64,13 +52,14 @@ struct EyebrowHeadlineBodyLinkModel: MoleculeProtocol { headline.setWithModel(casteModel?.headline, delegateObject, additionalData) body.setWithModel(casteModel?.body, delegateObject, additionalData) link.setWithModel(casteModel?.link, delegateObject, additionalData) - - (stack.stackItems[0].model as? StackItemModel)?.gone = !eyebrow.hasText - (stack.stackItems[1].model as? StackItemModel)?.gone = !headline.hasText - (stack.stackItems[2].model as? StackItemModel)?.gone = !body.hasText - (stack.stackItems[3].model as? StackItemModel)?.gone = ((link.titleLabel?.text?.count) ?? 0) == 0 + + // Create a stack model to use for the internal stack. + let stackModel = StackModel(molecules: [StackItemModel(gone: !eyebrow.hasText),StackItemModel(gone: !headline.hasText),StackItemModel(gone: !body.hasText),StackItemModel(gone: (link.titleLabel?.text?.count ?? 0) == 0)]) + stackModel.spacing = 0 + stack.model = stackModel stack.restack() } + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: EyebrowHeadlineBodyLinkModel.self) else { return } setWithModel(model, delegateObject, additionalData) diff --git a/MVMCoreUI/Organisms/MoleculeStackModel.swift b/MVMCoreUI/Organisms/MoleculeStackModel.swift index 2a149e73..7136718c 100644 --- a/MVMCoreUI/Organisms/MoleculeStackModel.swift +++ b/MVMCoreUI/Organisms/MoleculeStackModel.swift @@ -5,17 +5,19 @@ // Created by Suresh, Kamlesh on 10/3/19. // Copyright © 2019 Suresh, Kamlesh. All rights reserved. // +// A stack that has a list molecule stack items. import Foundation -@objcMembers public class MoleculeStackModel: ContainerModel, MoleculeProtocol { +@objcMembers public class MoleculeStackModel: ContainerModel, MoleculeProtocol, StackModelProtocol { public static var identifier: String = "stack" public var backgroundColor: Color? - public var molecules: [StackItemModel] + public var molecules: [MoleculeStackItemModel] public var axis: NSLayoutConstraint.Axis = .vertical public var spacing: CGFloat = 16.0 + public var useStackSpacingBeforeFirstItem = false - public init(molecules: [StackItemModel]) { + public init(molecules: [MoleculeStackItemModel]) { self.molecules = molecules super.init() } @@ -29,7 +31,7 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: StackCodingKeys.self) - molecules = try typeContainer.decode([StackItemModel].self, forKey: .molecules) + molecules = try typeContainer.decode([MoleculeStackItemModel].self, forKey: .molecules) if let axisString = try typeContainer.decodeIfPresent(String.self, forKey: .axis), let optionalAxis = NSLayoutConstraint.Axis(rawValue: axisString) { axis = optionalAxis } diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 9f2f526f..2674de0a 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -8,193 +8,22 @@ import UIKit -open class MoleculeStackView: Container { - var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() - var useStackSpacingBeforeFirstItem = false - var stackModel: MoleculeStackModel? { +open class MoleculeStackView: Stack { + override var stackModel: MoleculeStackModel? { get { return model as? MoleculeStackModel } } - var stackItems: [StackItem] = [] - var moleculesShouldSetHorizontalMargins = false - var moleculesShouldSetVerticalMargins = false - - // MARK: - Helpers - 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 - } - - /// Restacks the existing items. - func restack() { - removeAllItemViews() - let stackItems = self.stackItems - self.stackItems = [] - let lastItem = stackItems.last(where: { (item) -> Bool in - return !item.stackItemModel!.gone - }) - for item in stackItems { - addStackItem(item, lastItem: item === lastItem) - } - } - - /// Removes all stack items views from the view. - func removeAllItemViews() { - for item in stackItems { - item.removeFromSuperview() - } + /// Convenience function, adds a molecule to a MoleculeStackItem to the MoleculeStack + func addMolecule(_ view: View, lastItem: Bool) { + guard let model = view.model else { return } + let stackItemModel = MoleculeStackItemModel(with: model) + let stackItem = MoleculeStackItem(andContain: view) + addView(stackItem, stackItemModel, lastItem: lastItem) } - // 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 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 - } - MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) - translatesAutoresizingMaskIntoConstraints = false - backgroundColor = .clear - addSubview(contentView) - containerHelper.constrainView(contentView) - contentView.setContentHuggingPriority(.defaultHigh, for: .vertical) - contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - } - - public override func updateView(_ size: CGFloat) { - super.updateView(size) - for item in stackItems { - item.updateView(size) - } - } - - // MARK: - MVMCoreUIMoleculeViewProtocol - public override func reset() { - super.reset() - backgroundColor = .clear - for item in stackItems { - item.reset() - } - } - - public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - let previousModel = stackModel - super.setWithModel(model, delegateObject, additionalData) - removeAllItemViews() - - // If the items in the stack are different, clear them, create new ones. - if (previousModel == nil) || MoleculeStackView.nameForReuse(previousModel, delegateObject) != MoleculeStackView.nameForReuse(model, delegateObject) { - stackItems = [] - createStackItemsFromModel(with: delegateObject) - } else if let models = stackModel?.molecules { - for (index, element) in models.enumerated() { - stackItems[index].setWithModel(element, delegateObject, additionalData) - } - } - - restack() - stackModel?.useHorizontalMargins = moleculesShouldSetHorizontalMargins - stackModel?.useVerticalMargins = moleculesShouldSetVerticalMargins - } - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - if model == nil { - let data = try! JSONSerialization.data(withJSONObject: json!) - let decoder = JSONDecoder() - let model = try! decoder.decode(MoleculeStackModel.self, from: data) - setWithModel(model, delegateObject, additionalData) - } else { - setWithModel(model, delegateObject, additionalData) - } - } - - public override class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - // This will aggregate names of molecules to make an id. - guard let model = model as? MoleculeStackModel else { - return "stack<>" - } - var name = "stack<" - for case let item in model.molecules { - if let moleculeName = item.molecule.moleculeName { - if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol.Type, - let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { - name.append(nameForReuse + ",") - } else { - name.append(moleculeName + ",") - } - } - } - name.append(">") - return name - } - - 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<>" - } - var name = "stack<" - for case let item as [AnyHashable: Any] in molecules { - if let molecule = item.optionalDictionaryForKey(KeyMolecule), let moleculeName = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) { - name.append(moleculeName + ",") - } - } - name.append(">") - return name - } - - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let items = json?.optionalArrayForKey(KeyMolecules) else { - return 0 - } - let horizontal = json?.optionalStringForKey("axis") == "horizontal" - var estimatedHeight: CGFloat = 0 - for case let item as [AnyHashable: AnyHashable] in items { - if let molecule = item.optionalDictionaryForKey(KeyMolecule) { - let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.estimatedHeight?(forRow: molecule, delegateObject: delegateObject) - if !horizontal { - // Vertical stack aggregates the items - let spacing = item.optionalCGFloatForKey("spacing") ?? (estimatedHeight != 0 ? (json?.optionalCGFloatForKey("spacing") ?? 16) : 0) - estimatedHeight += ((height ?? 0) + spacing) - } else if let height = height { - // Horizontal stack takes the tallest item. - estimatedHeight = max(estimatedHeight, height) - } - } - } - return estimatedHeight - } - - public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - guard let items = json?.optionalArrayForKey(KeyMolecules) else { - return nil - } - var modules: [String] = [] - for case let item as [AnyHashable: AnyHashable] in items { - if let molecule = item.optionalDictionaryForKey(KeyMolecule), let modulesForMolecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.requiredModules?(molecule, delegateObject: delegateObject, error: error) { - modules += modulesForMolecule - } - } - return modules.count > 0 ? modules : nil - } - // MARK: - Adding to stack /// Creates all of the stackItems for the stackItemModels - func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { + override func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { guard let stackItemModels = stackModel?.molecules else { return } for model in stackItemModels { if let stackItem = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model, delegate) as? StackItem { @@ -202,67 +31,4 @@ open class MoleculeStackView: Container { } } } - - /// Adds the view to the stack. - func addView(_ view: View, lastItem: Bool) { - guard let model = view.model else { return } - let stackItem = StackItem(andContain: view) - stackItem.model = StackItemModel(with: model) - addStackItem(stackItem, lastItem: lastItem) - } - - /// Adds the stack item view - private func addStackItem(_ stackItem: StackItem, lastItem: Bool) { - let stackModel = self.stackModel! - let model = stackItem.stackItemModel! - guard !model.gone else { - // Gone views do not show - return - } - contentView.addSubview(stackItem) - stackItem.translatesAutoresizingMaskIntoConstraints = false - - let spacing = model.spacing ?? stackModel.spacing - let verticalAlignment = model.verticalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() ?? (model.percent == nil && stackModel.axis == .vertical ? .fill : (stackModel.axis == .vertical ? .leading : .center)) - let horizontalAlignment = model.horizontalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (stackModel.axis == .vertical || model.percent == nil ? .fill : .leading) - stackItem.containerHelper.alignHorizontal(horizontalAlignment) - stackItem.containerHelper.alignVertical(verticalAlignment) - - let first = stackItems.first { !($0.stackItemModel?.gone ?? false) } == nil - if stackModel.axis == .vertical { - if first { - pinView(stackItem, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) - } else if let previousView = stackItems.last(where: { item in - return !item.stackItemModel!.gone - }) { - stackItem.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: spacing).isActive = true - } - pinView(stackItem, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0) - pinView(contentView, toView: stackItem, attribute: .trailing, relation: .equal, priority: .required, constant: 0) - if let percent = model.percent { - stackItem.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0).isActive = true - } - if lastItem { - pinView(contentView, toView: stackItem, attribute: .bottom, relation: .equal, priority: .required, constant: 0) - } - } else { - if first { - // First horizontal item has no spacing by default unless told otherwise. - pinView(stackItem, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) - } else if let previousView = stackItems.last(where: { item in - return !item.stackItemModel!.gone - }) { - stackItem.leftAnchor.constraint(equalTo: previousView.rightAnchor, constant: spacing).isActive = true - } - pinView(stackItem, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) - pinView(contentView, toView: stackItem, attribute: .bottom, relation: .equal, priority: .required, constant: 0) - if let percent = model.percent { - stackItem.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0).isActive = true - } - if lastItem { - pinView(contentView, toView: stackItem, attribute: .right, relation: .equal, priority: .required, constant: 0) - } - } - stackItems.append(stackItem) - } } diff --git a/MVMCoreUI/Organisms/Stack.swift b/MVMCoreUI/Organisms/Stack.swift new file mode 100644 index 00000000..29f9cd9f --- /dev/null +++ b/MVMCoreUI/Organisms/Stack.swift @@ -0,0 +1,239 @@ +// +// Stack.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class Stack: Container where T: StackModelProtocol { + var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() + var stackModel: T? { + get { return model as? T } + } + var stackItems: [UIView] = [] + + // MARK: - Helpers + 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 + } + + /// Restacks the existing items. + func restack() { + removeAllItemViews() + guard let stackModel = stackModel else { return } + let stackItems = self.stackItems + self.stackItems = [] + let lastItemIndex = stackModel.molecules.lastIndex(where: { (item) -> Bool in + return !item.gone + }) + for (index, view) in stackItems.enumerated() { + addView(view, stackModel.molecules[index], lastItem: lastItemIndex == index) + } + } + + /// Removes all stack items views from the view. + func removeAllItemViews() { + for item in stackItems { + item.removeFromSuperview() + } + } + + // 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 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 + } + MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) + translatesAutoresizingMaskIntoConstraints = false + backgroundColor = .clear + addSubview(contentView) + containerHelper.constrainView(contentView) + contentView.setContentHuggingPriority(.defaultHigh, for: .vertical) + contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal) + } + + public override func updateView(_ size: CGFloat) { + super.updateView(size) + for item in stackItems { + (item as? MVMCoreViewProtocol)?.updateView(size) + } + } + + // MARK: - MVMCoreUIMoleculeViewProtocol + public override func reset() { + super.reset() + backgroundColor = .clear + for item in stackItems { + (item as? MoleculeViewProtocol)?.reset?() + } + } + + public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + let previousModel = self.model + super.setWithModel(model, delegateObject, additionalData) + removeAllItemViews() + + // If the items in the stack are different, clear them, create new ones. + if (previousModel == nil) || MoleculeStackView.nameForReuse(previousModel, delegateObject) != MoleculeStackView.nameForReuse(model, delegateObject) { + stackItems = [] + createStackItemsFromModel(with: delegateObject) + } else if let models = stackModel?.molecules { + for (index, element) in models.enumerated() { + (stackItems[index] as? ModelMoleculeViewProtocol)?.setWithModel(element as? MoleculeProtocol, delegateObject, additionalData) + } + } + + restack() + } + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if model == nil { + let data = try! JSONSerialization.data(withJSONObject: json!) + let decoder = JSONDecoder() + let model = try! decoder.decode(MoleculeStackModel.self, from: data) + setWithModel(model, delegateObject, additionalData) + } else { + setWithModel(model, delegateObject, additionalData) + } + } + + public override class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + // This will aggregate names of molecules to make an id. + guard let model = model as? MoleculeStackModel else { + return "stack<>" + } + var name = "stack<" + for case let item in model.molecules { + if let moleculeName = item.molecule.moleculeName { + if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol.Type, + let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { + name.append(nameForReuse + ",") + } else { + name.append(moleculeName + ",") + } + } + } + name.append(">") + return name + } + + // Need to update to take into account first spacing flag + public override class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let model = molecule as? MoleculeStackModel else { return 0 } + let horizontal = model.axis == .horizontal + var estimatedHeight: CGFloat = 0 + for case let item in model.molecules { + if item.gone { continue } + let height = (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(item) as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(forRow: item, delegateObject: delegateObject) ?? 0 + if !horizontal { + // Vertical stack aggregates the items + let spacing = item.spacing ?? model.spacing + estimatedHeight += (height + spacing) + } else { + // Horizontal stack takes the tallest item. + estimatedHeight = max(estimatedHeight, height) + } + } + return estimatedHeight + } + + public override class func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + guard let model = molecule as? MoleculeStackModel else { return nil } + var modules: [String] = [] + for case let item in model.molecules { + if let modulesForMolecule = (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(item) as? ModelMoleculeViewProtocol.Type)?.requiredModules(item, delegateObject: delegateObject, error: error) { + modules += modulesForMolecule + } + } + return modules.count > 0 ? modules : nil + } + + // MARK: - Adding to stack + /// Can be subclassed to create views when we get stack item models and have no views yet + func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { + } + + /// Convenience function, adds a view to a StackItem to the Stack + func addViewToItemToStack(_ view: UIView, lastItem: Bool) { + let stackItemModel = StackItemModel() + let stackItem = StackItem(andContain: view) + addView(stackItem, stackItemModel, lastItem: lastItem) + } + + /// Adds the stack item view + func addView(_ view: UIView,_ model: StackItemModelProtocol, lastItem: Bool) { + let stackModel = self.stackModel! + guard !model.gone else { + // Gone views do not show + stackItems.append(view) + return + } + contentView.addSubview(view) + view.translatesAutoresizingMaskIntoConstraints = false + + let spacing = model.spacing ?? stackModel.spacing + if let container = view as? ContainerProtocol { + let verticalAlignment = (model as? ContainerModelProtocol)?.verticalAlignment ?? (view as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() ?? (model.percent == nil && stackModel.axis == .vertical ? .fill : (stackModel.axis == .vertical ? .leading : .center)) + let horizontalAlignment = (model as? ContainerModelProtocol)?.horizontalAlignment ?? (view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (stackModel.axis == .vertical || model.percent == nil ? .fill : .leading) + container.alignHorizontal(horizontalAlignment) + container.alignVertical(verticalAlignment) + } + + let first = contentView.subviews.count == 1 + if stackModel.axis == .vertical { + if first { + pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: stackModel.useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) + } else if let previousView = stackItems.last(where: { item in + return !model.gone + }) { + view.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: spacing).isActive = 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) + if let percent = model.percent { + view.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0).isActive = true + } + if lastItem { + pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) + } + } else { + if first { + // First horizontal item has no spacing by default unless told otherwise. + pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: stackModel.useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) + } else if let previousView = stackItems.last(where: { item in + return !model.gone + }) { + view.leftAnchor.constraint(equalTo: previousView.rightAnchor, constant: spacing).isActive = true + } + pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) + pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) + if let percent = model.percent { + view.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0).isActive = true + } + if lastItem { + pinView(contentView, toView: view, attribute: .right, relation: .equal, priority: .required, constant: 0) + } + } + stackItems.append(view) + } +} diff --git a/MVMCoreUI/Organisms/StackModel.swift b/MVMCoreUI/Organisms/StackModel.swift new file mode 100644 index 00000000..b647b791 --- /dev/null +++ b/MVMCoreUI/Organisms/StackModel.swift @@ -0,0 +1,22 @@ +// +// StackModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class StackModel: StackModelProtocol, MoleculeProtocol { + public static var identifier: String = "simpleStack" + public var backgroundColor: Color? + public var molecules: [StackItemModel] + @Axis public var axis: NSLayoutConstraint.Axis = .vertical + public var spacing: CGFloat = 16.0 + public var useStackSpacingBeforeFirstItem = false + + public init(molecules: [StackItemModel]) { + self.molecules = molecules + } +} diff --git a/MVMCoreUI/Organisms/StackModelProtocol.swift b/MVMCoreUI/Organisms/StackModelProtocol.swift new file mode 100644 index 00000000..b7385466 --- /dev/null +++ b/MVMCoreUI/Organisms/StackModelProtocol.swift @@ -0,0 +1,18 @@ +// +// StackModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol StackModelProtocol { + associatedtype AnyStackItemModel: StackItemModelProtocol + + var molecules: [AnyStackItemModel] { get set } + var axis: NSLayoutConstraint.Axis { get set } + var spacing: CGFloat { get set } + var useStackSpacingBeforeFirstItem: Bool { get set } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 6c3e139e..ab054232 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -31,7 +31,6 @@ @"button": PrimaryButton.class, @"link": MFTextButton.class, @"header": StandardHeaderView.class, - @"stack": MoleculeStackView.class, @"twoButtonView": TwoButtonView.class, @"footer": StandardFooterView.class, @"caretView": CaretView.class, diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index 86e7ee7e..ddc2d13f 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -10,11 +10,13 @@ import Foundation @objcMembers public class MoleculeObjectMapping: NSObject { public static func registerObjects() { + MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(MoleculeStackView.self, forKey: "stack" as NSString) + ModelRegistry.register(LabelModel.self) ModelRegistry.register(HeaderModel.self) ModelRegistry.register(HeadlineBodyModel.self) ModelRegistry.register(MoleculeStackModel.self) - ModelRegistry.register(StackItemModel.self) + ModelRegistry.register(MoleculeStackItemModel.self) ModelRegistry.register(TextFieldModel.self) ModelRegistry.register(ProgressBarModel.self) ModelRegistry.register(MultiProgressBarModel.self) diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index a4f69518..03d21c6b 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -49,8 +49,8 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { } let stack = MoleculeStackView(frame: .zero) - stack.useStackSpacingBeforeFirstItem = true - stack.moleculesShouldSetHorizontalMargins = true + moleculeStackModel.useStackSpacingBeforeFirstItem = true + moleculeStackModel.useHorizontalMargins = true stack.setWithModel(moleculeStackModel, delegateObject() as? MVMCoreUIDelegateObject, nil) return stack } diff --git a/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift b/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift index 05d13bcd..f989e17b 100644 --- a/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift +++ b/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift @@ -30,6 +30,11 @@ import Foundation try container.encode(axis.rawValueString, forKey: .axis) } */ + +enum AxisError: Error { + case notAnAxis +} + extension NSLayoutConstraint.Axis: RawRepresentable { init?(rawValue: String) { @@ -54,3 +59,29 @@ extension NSLayoutConstraint.Axis: RawRepresentable { } } } + +@propertyWrapper +public struct Axis { + public var wrappedValue: NSLayoutConstraint.Axis + + public init(wrappedValue value: NSLayoutConstraint.Axis) { + self.wrappedValue = value + } +} + +extension Axis: Codable { + public init(from decoder: Decoder) throws { + let typeContainer = try decoder.singleValueContainer() + let string = try typeContainer.decode(String.self) + guard let axis = NSLayoutConstraint.Axis(rawValue: string) else { + throw AxisError.notAnAxis + } + wrappedValue = axis + } + + public func encode(to encoder: Encoder) throws { + let string = wrappedValue.rawValueString + var container = encoder.singleValueContainer() + try container.encode(string) + } +} From 7d5bee5654525ed5dc7af19c73c66e65078abf5b Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 15:51:15 -0500 Subject: [PATCH 10/19] Label fix --- MVMCoreUI/Atoms/Views/Label.swift | 17 ++++++++++++++--- .../LabelModel/LabelAttributeActionModel.swift | 11 ++++++++++- .../Molecules/Doughnut/DoughnutChart.swift | 9 ++++----- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index e5491baf..b4173add 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -244,10 +244,15 @@ public typealias ActionBlock = () -> () } if let fontStyle = labelModel.fontStyle { MFStyler.styleLabel(self, withStyle: fontStyle) + MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false) + standardFontSize = font.pointSize } else { let fontSize = labelModel.fontSize + if let fontSize = fontSize { + standardFontSize = fontSize + } if let fontName = labelModel.fontName { - font = MFFonts.mfFont(withName: fontName, size: fontSize ?? font.pointSize) + font = MFFonts.mfFont(withName: fontName, size: fontSize ?? standardFontSize) } else if let fontSize = fontSize { font = font.withSize(fontSize) } @@ -262,9 +267,9 @@ public typealias ActionBlock = () -> () for attribute in attributes { let range = NSRange(location: attribute.location, length: attribute.length) switch attribute { - case let underLineAtt as LabelAttributeUnderlineModel: + case let _ as LabelAttributeUnderlineModel: attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) - case let strikeAtt as LabelAttributeStrikeThroughModel: + case let _ as LabelAttributeStrikeThroughModel: attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) attributedString.addAttribute(.baselineOffset, value: 0, range: range) case let colorAtt as LabelAttributeColorModel: @@ -309,11 +314,17 @@ public typealias ActionBlock = () -> () } } case let actionAtt as LabelAttributeActionModel: + addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) { + if let data = try? actionAtt.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } addActionAttributes(range: range, string: attributedString) default: continue } } + attributedText = attributedString originalAttributedString = attributedText hero = labelModel.hero } diff --git a/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift b/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift index eb5a3cd7..8dd5c562 100644 --- a/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift +++ b/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift @@ -12,12 +12,21 @@ class LabelAttributeActionModel: LabelAttributeModel { override public class var identifier: String { return "action" } - + var action: ActionProtocol + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + action = try typeContainer.decodeModel(codingKey: .action, typeCodingKey: ActionCodingKey.actionType) try super.init(from: decoder) } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModel(action, forKey: .action) + } + + private enum CodingKeys: String, CodingKey { + case action } } diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index a3961833..acb2e9ea 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -179,10 +179,9 @@ open class DoughnutChart: View { let labelheight = labelContainer.frame.height/2 let padding = sizeObject.getValueBasedOnApplicationWidth()/2 - sqrt(pow(radius, 2) - pow(labelheight, 2)) - labelContainer.leftPin?.constant = padding - labelContainer.rightPin?.constant = padding - labelContainer.topPin?.constant = max(radius - labelheight, labelheight) - labelContainer.bottomPin?.constant = max(radius - labelheight, labelheight) + labelContainerLeftConstraint?.constant = padding + labelContainerRightConstraint?.constant = padding + labelContainerTopConstraint?.constant = max(radius - labelheight, labelheight) + labelContainerBottomConstraint?.constant = max(radius - labelheight, labelheight) } - } From cb89795b4cb78ba86fa43b40333557cebeb27413 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 16:17:36 -0500 Subject: [PATCH 11/19] remove ! --- MVMCoreUI/Organisms/Stack.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Organisms/Stack.swift b/MVMCoreUI/Organisms/Stack.swift index 29f9cd9f..0fc1ca8f 100644 --- a/MVMCoreUI/Organisms/Stack.swift +++ b/MVMCoreUI/Organisms/Stack.swift @@ -107,12 +107,11 @@ open class Stack: Container where T: StackModelProtocol { } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - if model == nil { - let data = try! JSONSerialization.data(withJSONObject: json!) - let decoder = JSONDecoder() - let model = try! decoder.decode(MoleculeStackModel.self, from: data) + if let model = model { setWithModel(model, delegateObject, additionalData) - } else { + } else if let json = json, + let data = try? JSONSerialization.data(withJSONObject: json), + let model = try? JSONDecoder().decode(MoleculeStackModel.self, from: data) { setWithModel(model, delegateObject, additionalData) } } @@ -182,7 +181,7 @@ open class Stack: Container where T: StackModelProtocol { /// Adds the stack item view func addView(_ view: UIView,_ model: StackItemModelProtocol, lastItem: Bool) { - let stackModel = self.stackModel! + guard let stackModel = self.stackModel else { return } guard !model.gone else { // Gone views do not show stackItems.append(view) From 36cbccd39c0e5efb0f11dcaea6d609408d9c964c Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Thu, 16 Jan 2020 16:30:05 -0500 Subject: [PATCH 12/19] update name --- MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift | 4 ++-- MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index acb2e9ea..233ca7d7 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -135,7 +135,7 @@ open class DoughnutChart: View { let clockwise = true let value: CGFloat = chartModel.percent - let gap: CGFloat = spaceReuired() ? lineGap() : 0.0 + let gap: CGFloat = spaceRequired() ? lineGap() : 0.0 let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap let circlePath = UIBezierPath(arcCenter: arcCenter, @@ -168,7 +168,7 @@ open class DoughnutChart: View { return doughnutChartModel?.sections.count == 1 ? 0.0 : 0.05 } - func spaceReuired() -> Bool { + func spaceRequired() -> Bool { return doughnutChartModel?.spaceRequired ?? true } diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift index 4893edbc..31091796 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift @@ -50,7 +50,7 @@ import Foundation centerY.isActive = true colorLablesStack.leadingAnchor.constraint(equalTo: doughnutChart.trailingAnchor, constant: PaddingThree).isActive = true - colorLablesStack.backgroundColor = UIColor.clear + colorLablesStack.backgroundColor = .clear } open override func updateView(_ size: CGFloat) { From b1abe0810cf78c9db39df2868d5bad64483152eb Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Thu, 16 Jan 2020 16:43:16 -0500 Subject: [PATCH 13/19] put clockwise into method --- MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index 233ca7d7..130536e4 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -132,8 +132,7 @@ open class DoughnutChart: View { let arcCenter = shapeLayer.position let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 - let clockwise = true - + let value: CGFloat = chartModel.percent let gap: CGFloat = spaceRequired() ? lineGap() : 0.0 let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) @@ -142,7 +141,7 @@ open class DoughnutChart: View { radius: radius, startAngle: startAngle, endAngle: endAngle, - clockwise: clockwise) + clockwise: true) shapeLayer.path = circlePath.cgPath doughnutLayer.addSublayer(shapeLayer) From 3cfe5aa4237a1f6ce6bb0d05aa9c53f49a21f0da Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 16:47:53 -0500 Subject: [PATCH 14/19] StackModel --- MVMCoreUI/Organisms/StackModel.swift | 29 +++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Organisms/StackModel.swift b/MVMCoreUI/Organisms/StackModel.swift index b647b791..deea2457 100644 --- a/MVMCoreUI/Organisms/StackModel.swift +++ b/MVMCoreUI/Organisms/StackModel.swift @@ -12,11 +12,38 @@ import Foundation public static var identifier: String = "simpleStack" public var backgroundColor: Color? public var molecules: [StackItemModel] - @Axis public var axis: NSLayoutConstraint.Axis = .vertical + public var axis: NSLayoutConstraint.Axis = .vertical public var spacing: CGFloat = 16.0 public var useStackSpacingBeforeFirstItem = false public init(molecules: [StackItemModel]) { self.molecules = molecules } + + enum StackCodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case molecules + case axis + case spacing + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: StackCodingKeys.self) + + molecules = try typeContainer.decode([StackItemModel].self, forKey: .molecules) + if let axisString = try typeContainer.decodeIfPresent(String.self, forKey: .axis), let optionalAxis = NSLayoutConstraint.Axis(rawValue: axisString) { + axis = optionalAxis + } + if let spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) { + self.spacing = spacing + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StackCodingKeys.self) + try container.encodeIfPresent(molecules, forKey: .molecules) + try container.encodeIfPresent(axis.rawValueString, forKey: .axis) + try container.encodeIfPresent(spacing, forKey: .spacing) + } } From 8209101b73683a59ab27cde568fee99fe0f5d230 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 18:54:01 -0500 Subject: [PATCH 15/19] hot fix --- MVMCoreUI/Molecules/Items/MoleculeStackItem.swift | 2 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 2 +- MVMCoreUI/OtherHandlers/CoreUIObject.swift | 1 + .../OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 10 +--------- MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift | 3 ++- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift index d883f93e..279e7948 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift @@ -1,5 +1,5 @@ // -// StackItem.swift +// MoleculeStackItem.swift // MVMCoreUI // // Created by Scott Pfeil on 12/13/19. diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 2674de0a..a7dd1640 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -26,7 +26,7 @@ open class MoleculeStackView: Stack { override func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { guard let stackItemModels = stackModel?.molecules else { return } for model in stackItemModels { - if let stackItem = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model, delegate) as? StackItem { + if let stackItem = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model, delegate) as? MoleculeStackItem { stackItems.append(stackItem) } } diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index af0f5023..427fcfba 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -19,5 +19,6 @@ import UIKit viewControllerMapping = MVMCoreUIViewControllerMappingObject() loggingDelegate = MVMCoreUILoggingHandler() moleculeMap = MVMCoreUIMoleculeMappingObject() + MoleculeObjectMapping.registerObjects() } } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 09c98c1a..1eda2454 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -70,10 +70,9 @@ @"tabsListItem": TabsTableViewCell.class, @"dropDownListItem": DropDownFilterTableViewCell.class, @"headlineBodyButton": HeadlineBodyButton.class, - @"stackItem": StackItem.class, + @"stackItem": MoleculeStackItem.class, @"eyebrowHeadlineBodyLink": EyebrowHeadlineBodyLink.class, @"headlineBodyCaretLinkImage" : HeadLineBodyCaretLinkImage.class, - @"stackItem": StackItem.class, @"doughnutChart": DoughnutChartView.class, @"headLineBodyCaretLinkImage" : HeadLineBodyCaretLinkImage.class } mutableCopy]; @@ -85,13 +84,6 @@ return [MVMCoreActionUtility initializerClassCheck:[CoreUIObject sharedInstance].moleculeMap classToVerify:self]; } -- (instancetype)init { - if (self = [super init]) { - [MoleculeObjectMapping registerObjects]; - } - return self; -} - - (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json { NSString *moleculeName = [json string:KeyMoleculeName]; if (moleculeName) { diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index fe4c7e11..6bcdd4cd 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -10,7 +10,8 @@ import Foundation @objcMembers public class MoleculeObjectMapping: NSObject { public static func registerObjects() { - MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(MoleculeStackView.self, forKey: "stack" as NSString) + let mapping = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping + mapping?.setObject(MoleculeStackView.self, forKey: "stack" as NSString) ModelRegistry.register(LabelModel.self) ModelRegistry.register(HeaderModel.self) From 54cc1f54261e76fb2d126bb8fb4a3f4b86815129 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 19:48:14 -0500 Subject: [PATCH 16/19] registerModels() --- MVMCoreUI/OtherHandlers/CoreUIObject.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index 427fcfba..9d2957c2 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -10,7 +10,11 @@ import UIKit @objcMembers open class CoreUIObject: MVMCoreObject { public var moleculeMap: MVMCoreUIMoleculeMappingObject? - + + open func registerModels() { + MoleculeObjectMapping.registerObjects() + } + open override func defaultInitialSetup() { cache = MVMCoreCache() sessionHandler = MVMCoreSessionTimeHandler() @@ -19,6 +23,6 @@ import UIKit viewControllerMapping = MVMCoreUIViewControllerMappingObject() loggingDelegate = MVMCoreUILoggingHandler() moleculeMap = MVMCoreUIMoleculeMappingObject() - MoleculeObjectMapping.registerObjects() + registerModels() } } From d8a7efde8a727fb0f6f9849041ebacfdfd69c669 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 20:57:17 -0500 Subject: [PATCH 17/19] undo register change --- MVMCoreUI/OtherHandlers/CoreUIObject.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index 9d2957c2..d60cdd6a 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -11,10 +11,6 @@ import UIKit @objcMembers open class CoreUIObject: MVMCoreObject { public var moleculeMap: MVMCoreUIMoleculeMappingObject? - open func registerModels() { - MoleculeObjectMapping.registerObjects() - } - open override func defaultInitialSetup() { cache = MVMCoreCache() sessionHandler = MVMCoreSessionTimeHandler() @@ -23,6 +19,6 @@ import UIKit viewControllerMapping = MVMCoreUIViewControllerMappingObject() loggingDelegate = MVMCoreUILoggingHandler() moleculeMap = MVMCoreUIMoleculeMappingObject() - registerModels() + MoleculeObjectMapping.registerObjects() } } From 017dc1d0020520a01d75bf887eaea9b8bbb3350d Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Fri, 17 Jan 2020 21:32:49 +0530 Subject: [PATCH 18/19] secondary button top alignment fix, which was breaking all BAU secondary buttons. --- .../Molecules/HorizontalCombinationViews/TwoButtonView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift b/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift index 10a41daa..47e67724 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift +++ b/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift @@ -93,7 +93,7 @@ import UIKit viewForButtons.addSubview(secondaryButton) secondaryButton.widthAnchor.constraint(equalTo: primaryButton.widthAnchor, multiplier: 1).isActive = true NSLayoutConstraint.constraintPinSubview(primaryButton, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) - NSLayoutConstraint.constraintPinSubview(secondaryButton, pinTop: false, pinBottom: false, pinLeft: false, pinRight: true) + NSLayoutConstraint.constraintPinSubview(secondaryButton, pinTop: true, pinBottom: false, pinLeft: false, pinRight: true) let constraint = secondaryButton.leadingAnchor.constraint(equalTo: primaryButton.trailingAnchor, constant: 10) constraint.priority = UILayoutPriority(900) constraint.isActive = true From 13dbb283571e42bf931a51130ff02e9b63c7a016 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 17 Jan 2020 11:04:35 -0500 Subject: [PATCH 19/19] caret title --- MVMCoreUI/Atoms/Buttons/CaretButton.swift | 2 +- MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/CaretButton.swift b/MVMCoreUI/Atoms/Buttons/CaretButton.swift index 6c8e4d31..64120043 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretButton.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretButton.swift @@ -149,7 +149,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI } isEnabled = caretLinkModel.enabled set(with: caretLinkModel.action, delegateObject: delegateObject, additionalData: additionalData) - setTitle(caretLinkModel.label.text, for: .normal) + setTitle(caretLinkModel.title, for: .normal) } public func needsToBeConstrained() -> Bool { diff --git a/MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift b/MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift index db2de2b0..b2fc5841 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift @@ -12,20 +12,20 @@ import MVMCore public class CaretLinkModel: MoleculeProtocol { public static var identifier: String = "caretLink" public var backgroundColor: Color? - public var label: LabelModel + public var title: String public var action: ActionProtocol public var enabledColor: Color = Color(uiColor: .black) public var disabledColor: Color? = Color(uiColor: .mfSilver()) public var enabled: Bool = true - public init(label: LabelModel, action: ActionProtocol) { - self.label = label + public init(title: String, action: ActionProtocol) { + self.title = title self.action = action } enum CodingKeys: String, CodingKey { case backgroundColor - case label + case title case action case enabledColor case disabledColor @@ -35,7 +35,7 @@ public class CaretLinkModel: MoleculeProtocol { required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - label = try typeContainer.decode(LabelModel.self, forKey: .label) + title = try typeContainer.decode(String.self, forKey: .title) if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor) { enabledColor = color } @@ -50,7 +50,7 @@ public class CaretLinkModel: MoleculeProtocol { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(label, forKey: .label) + try container.encode(title, forKey: .title) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeModel(action, forKey: .action) try container.encode(enabled, forKey: .enabledColor)