diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 33daa259..ffbc8621 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -202,6 +202,7 @@ D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; + D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */; }; D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22479892314445E003FCCF9 /* LabelToggle.swift */; }; D224798C231450C8003FCCF9 /* HeadlineBodyToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224798B231450C8003FCCF9 /* HeadlineBodyToggle.swift */; }; @@ -589,6 +590,7 @@ D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; + D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraintAxis+Extension.swift"; sourceTree = ""; }; D22479892314445E003FCCF9 /* LabelToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelToggle.swift; sourceTree = ""; }; D224798B231450C8003FCCF9 /* HeadlineBodyToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyToggle.swift; sourceTree = ""; }; @@ -1181,6 +1183,7 @@ D260105A23D0BB7100764D80 /* StackModelProtocol.swift */, D260106423D0CEA700764D80 /* StackModel.swift */, D260105C23D0BCD400764D80 /* Stack.swift */, + D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */, 012A88AE238C626E00FE3DA1 /* CarouselModel.swift */, D2A6390022CBB1820052ED1F /* Carousel.swift */, ); @@ -1924,6 +1927,7 @@ D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */, D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */, + D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */, 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift b/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift index 67a5f840..6339d002 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift @@ -27,23 +27,6 @@ import UIKit super.init(frame: frame) } - ///passing value to progressList creates corresponding progress bars - var progressList: Array? { - didSet { - stack.removeAllItemViews() - guard (progressList?.count ?? 0) > 0 else { return } - var viewModels: [(view: UIView, model: StackItemModel)] = [] - for progressObject in progressList! { - guard progressObject.percent > 0.0 else { continue } - let model = StackItemModel(spacing: 0, percent: Int(progressObject.percent), horizontalAlignment: .fill, verticalAlignment: .fill) - model.backgroundColor = progressObject.color - viewModels.append((view: StackItem(frame: .zero), model: model)) - } - stack.set(with: viewModels) - stack.restack() - } - } - var roundedCorners: Bool = false { didSet { if roundedCorners { @@ -62,30 +45,51 @@ import UIKit translatesAutoresizingMaskIntoConstraints = false backgroundColor = .mfLightSilver() clipsToBounds = true + addSubview(stack) NSLayoutConstraint.constraintPinSubview(toSuperview: stack) + stack.backgroundColor = backgroundColor + stack.contentView.backgroundColor = .white + stack.model = StackModel(molecules: [], axis: .horizontal, spacing: 2) + stack.stackModel?.horizontalAlignment = .leading + if thicknessConstraint == nil { thicknessConstraint = heightAnchor.constraint(equalToConstant: defaultHeight) thicknessConstraint?.isActive = true } } + /// Creates the bars + open func set(with progressList: Array, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let stackModel = stack.stackModel else { return } + var views: [StackItem] = [] + var models: [StackItemModel] = [] + for progressObject in progressList { + guard progressObject.percent > 0.0 else { continue } + let model = StackItemModel(percent: Int(progressObject.percent), horizontalAlignment: .fill, verticalAlignment: .fill) + model.backgroundColor = progressObject.color + models.append(model) + views.append(StackItem()) + } + stack.stackItems = views + stackModel.molecules = models + stack.set(with: stackModel, delegateObject, additionalData) + } + //MARK: - MoleculeViewProtocol public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let multiProgressModel = multiProgressModel else { - return - } + guard let multiProgressModel = multiProgressModel else { return } roundedCorners = multiProgressModel.roundedCorners ?? false thicknessConstraint?.constant = multiProgressModel.thickness ?? defaultHeight - progressList = multiProgressModel.progressList - + stack.model?.backgroundColor = model.backgroundColor + set(with: multiProgressModel.progressList, delegateObject, additionalData) } open override func reset() { super.reset() backgroundColor = .mfLightSilver() - progressList = nil + set(with: [], nil, nil) } public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 354bb082..ee26efaa 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -37,7 +37,7 @@ import Foundation /// Call to register all of the CoreUI molecules. public static func registerObjects() { // Stacks - MoleculeObjectMapping.shared()?.register(viewClass: Stack.self, viewModelClass: StackModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeStackView.self, viewModelClass: StackModel.self) MoleculeObjectMapping.shared()?.register(viewClass: UnOrderedList.self, viewModelClass: UnOrderedListModel.self) MoleculeObjectMapping.shared()?.register(viewClass: NumberedList.self, viewModelClass: NumberedListModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartView.swift b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartView.swift index bffea398..36bec9a5 100644 --- a/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartView.swift +++ b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartView.swift @@ -88,7 +88,7 @@ extension DoughnutChartView: MVMCoreUIViewConstrainingProtocol { } } -class ColorViewLabelsStack: Stack { +class ColorViewLabelsStack: MoleculeStackView { override func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { guard let stackItemModels = stackModel?.molecules else { return } for model in stackItemModels { diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift index 8bd6d005..c887e838 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift @@ -9,7 +9,7 @@ import UIKit // This class is only temporarily necessary. Eventually we will have initWithModel instad of just init for moleculeviews, which will remove this need. -open class StringAndMoleculeStack: Stack { +open class StringAndMoleculeStack: MoleculeStackView { override open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { guard let model = model as? StackModelProtocol, let molcules = model.molecules as? [MoleculeStackItemModel] else { return } diff --git a/MVMCoreUI/Atomic/Organisms/MoleculeStackView.swift b/MVMCoreUI/Atomic/Organisms/MoleculeStackView.swift new file mode 100644 index 00000000..c5c993f2 --- /dev/null +++ b/MVMCoreUI/Atomic/Organisms/MoleculeStackView.swift @@ -0,0 +1,61 @@ +// +// MoleculeStackView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 2/11/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// +// This is a dynamic stack view that creates molecules if needed on set. + +import UIKit + +open class MoleculeStackView: Stack { + var previousModel: MoleculeModelProtocol? + + /// Convenience function, adds a molecule to a MoleculeStackItem to the MoleculeStack + func setup(with views: [View], lastItem: Bool) { + var models: [MoleculeStackItemModel] = [] + for view in views { + guard let model = view.model else { return } + let stackItemModel = MoleculeStackItemModel(with: model) + let stackItem = MoleculeStackItem(andContain: view) + stackItems.append(stackItem) + models.append(stackItemModel) + } + if let stackModel = stackModel { + stackModel.molecules = models + } else { + model = StackModel(molecules: models) + } + restack() + } + + + + // MARK: - Adding to stack + /// Can be subclassed to create views when we get stack item models and have no views yet + open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + guard let stackItemModels = stackModel?.molecules else { return } + for model in stackItemModels { + if let stackItem = MoleculeObjectMapping.shared()?.createMolecule(model, delegateObject: delegateObject, additionalData: additionalData) as? MoleculeStackItem { + stackItems.append(stackItem) + } + } + } + + open override func setStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + // If the items in the stack are different, clear them, create new ones. + if (previousModel == nil) || Self.nameForReuse(with: previousModel!, delegateObject) != Self.nameForReuse(with: model, delegateObject) { + stackItems = [] + createStackItemsFromModel(model, delegateObject, additionalData) + } else { + super.setStackItemsFromModel(model, delegateObject, additionalData) + } + } + + /// + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + previousModel = self.model + super.set(with: model, delegateObject, additionalData) + } +} diff --git a/MVMCoreUI/Atomic/Organisms/Stack.swift b/MVMCoreUI/Atomic/Organisms/Stack.swift index ab17be3b..d43719a9 100644 --- a/MVMCoreUI/Atomic/Organisms/Stack.swift +++ b/MVMCoreUI/Atomic/Organisms/Stack.swift @@ -147,18 +147,9 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - let previousModel = self.model super.set(with: model, delegateObject, additionalData) removeAllItemViews() - - // If the items in the stack are different, clear them, create new ones. - if (previousModel == nil) || Self.nameForReuse(with: previousModel!, delegateObject) != Self.nameForReuse(with: model, delegateObject) { - stackItems = [] - createStackItemsFromModel(model, delegateObject, additionalData) - } else { - setStackItemsFromModel(model, delegateObject, additionalData) - } - + setStackItemsFromModel(model, delegateObject, additionalData) restack() } @@ -216,16 +207,6 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto // MARK: - Subclassables //-------------------------------------------------- - /// Can be subclassed to create views when we get stack item models and have no views yet - open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let stackItemModels = (model as? StackModelProtocol)?.molecules else { return } - for model in stackItemModels { - if let stackItem = MoleculeObjectMapping.shared()?.createMolecule(model, delegateObject: delegateObject, additionalData: additionalData) { - stackItems.append(stackItem) - } - } - } - /// Can be subclassed to set stack items with model when we already have views open func setStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let models = stackModel?.molecules else { return } @@ -314,7 +295,7 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto if let percent = model.percent { let multiplier = CGFloat(percent) / 100.0 let constant = multiplier * totalSpacing - view.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: multiplier, constant: -constant).isActive = true + view.heightAnchor.constraint(equalTo: heightAnchor, multiplier: multiplier, constant: -constant).isActive = true } if lastItem { pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) @@ -333,7 +314,7 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto if let percent = model.percent { let multiplier = CGFloat(percent) / 100.0 let constant = multiplier * totalSpacing - view.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: multiplier, constant: -constant).isActive = true + view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: multiplier, constant: -constant).isActive = true } if lastItem { pinView(contentView, toView: view, attribute: .right, relation: .equal, priority: .required, constant: 0)