From 04bbf58e6ae936e095dccda4203b2d38f09216d3 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 8 Sep 2020 13:13:43 +0530 Subject: [PATCH 01/31] Heart atom initial commit. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++ MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 84 +++++++++++++++++++ .../Atomic/Atoms/Selectors/HeartModel.swift | 61 ++++++++++++++ MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 2 +- 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b61c3aa1..133e099b 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -236,6 +236,8 @@ AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; + AAA7CD69250641F90045B959 /* HeartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA7CD68250641F90045B959 /* HeartModel.swift */; }; + AAA7CD6B250642080045B959 /* Heart.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA7CD6A250642080045B959 /* Heart.swift */; }; AAA905DF24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA905DE24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift */; }; AAA905E124D1759A00D1EFAB /* ListThreeColumnBillHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA905E024D1759A00D1EFAB /* ListThreeColumnBillHistory.swift */; }; AAB7EDEF246ADA1600E54929 /* ListProgressBarThinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB7EDEE246ADA1600E54929 /* ListProgressBarThinModel.swift */; }; @@ -717,6 +719,8 @@ AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconAllTextLinks.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.swift; sourceTree = ""; }; + AAA7CD68250641F90045B959 /* HeartModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeartModel.swift; sourceTree = ""; }; + AAA7CD6A250642080045B959 /* Heart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Heart.swift; sourceTree = ""; }; AAA905DE24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnBillHistoryModel.swift; sourceTree = ""; }; AAA905E024D1759A00D1EFAB /* ListThreeColumnBillHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnBillHistory.swift; sourceTree = ""; }; AAB7EDEE246ADA1600E54929 /* ListProgressBarThinModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListProgressBarThinModel.swift; sourceTree = ""; }; @@ -1564,6 +1568,8 @@ D2092348244A51D40044AD09 /* RadioSwatchModel.swift */, AAB9C109243496DD00151545 /* RadioSwatch.swift */, AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */, + AAA7CD68250641F90045B959 /* HeartModel.swift */, + AAA7CD6A250642080045B959 /* Heart.swift */, ); path = Selectors; sourceTree = ""; @@ -2187,6 +2193,7 @@ 943784F5236B77BB006A1E82 /* Wheel.swift in Sources */, 31BE15CC23D8924D00452370 /* CheckboxModel.swift in Sources */, 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */, + AAA7CD6B250642080045B959 /* Heart.swift in Sources */, 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */, D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */, @@ -2205,6 +2212,7 @@ BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, 3265B30424BCA749000D154B /* HeadersH1NoButtonsBodyText.swift in Sources */, + AAA7CD69250641F90045B959 /* HeartModel.swift in Sources */, D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */, D28BA7452481652D00B75CB8 /* TabBarProtocol.swift in Sources */, AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift new file mode 100644 index 00000000..33625303 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -0,0 +1,84 @@ +// +// Heart.swift +// MVMCoreUI +// +// Created by Lekshmi S on 07/09/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class Heart: Control { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + private var heartLayer: CAShapeLayer? + @objc public override var isSelected: Bool { + didSet { + heartModel?.isActive = isSelected + } + } + public var delegateObject: MVMCoreUIDelegateObject? + public var heartModel: HeartModel? { + return model as? HeartModel + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func draw(_ layer: CALayer, in ctx: CGContext) { + //Draw the heart + heartLayer?.removeFromSuperlayer() + let heart = drawHeart() + layer.addSublayer(heart) + heartLayer = heart + } + + func drawHeart() -> CAShapeLayer { + let heart = CAShapeLayer() + let rect = CGRect(x: 0, y: 0, width: 22, height: 22) + let leftArc = rect.width * 0.4 + let rightArc = rect.height * 0.3 + let arcRadius = sqrt(leftArc*leftArc + rightArc*rightArc)/2 + let heartPath = UIBezierPath() + //Left Hand Curve + heartPath.addArc(withCenter: CGPoint(x: rect.width * 0.3, y: rect.height * 0.35), radius: arcRadius, startAngle: 135.degreesToRadians, endAngle: 315.degreesToRadians, clockwise: true) + //Top Centre Dip + heartPath.addLine(to: CGPoint(x: rect.width/2, y: rect.height * 0.2)) + //Right Hand Curve + heartPath.addArc(withCenter: CGPoint(x: rect.width * 0.7, y: rect.height * 0.35), radius: arcRadius, startAngle: 225.degreesToRadians, endAngle: 45.degreesToRadians, clockwise: true) + //Right Bottom Line + heartPath.addLine(to: CGPoint(x: rect.width * 0.5, y: rect.height * 0.95)) + //Left Bottom Line + heartPath.close() + heart.path = heartPath.cgPath + heart.fillColor = isSelected ? heartModel?.activeColor.cgColor : heartModel?.inActiveColor.cgColor + heart.opacity = 1.0 + heart.lineWidth = 1 + heart.strokeColor = isSelected ? UIColor.clear.cgColor : UIColor.mvmBlack.cgColor + return heart + } + + //-------------------------------------------------- + // MARK: - MVMViewProtocol + //-------------------------------------------------- + open override func setupView() { + super.setupView() + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + layer.setNeedsDisplay() + } + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + self.delegateObject = delegateObject + guard let model = model as? HeartModel else { return } + isSelected = model.isActive + } +} + +extension Int { + var degreesToRadians: CGFloat { return CGFloat(self) * .pi / 180 } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift new file mode 100644 index 00000000..469dc4d9 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift @@ -0,0 +1,61 @@ +// +// HeartModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 07/09/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class HeartModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "heart" + public var backgroundColor: Color? + public var isActive: Bool = false + public var activeColor: Color = Color(uiColor: .mvmRed) + public var inActiveColor: Color = Color(uiColor: .clear) + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case isActive + case activeColor + case inActiveColor + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let isActive = try typeContainer.decodeIfPresent(Bool.self, forKey: .isActive) { + self.isActive = isActive + } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + if let activeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .activeColor) { + self.activeColor = activeColor + } + if let inActiveColor = try typeContainer.decodeIfPresent(Color.self, forKey: .inActiveColor) { + self.inActiveColor = inActiveColor + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(isActive, forKey: .isActive) + try container.encodeIfPresent(activeColor, forKey: .activeColor) + try container.encodeIfPresent(inActiveColor, forKey: .inActiveColor) + } +} diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index a08347c7..4016ec8a 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -87,7 +87,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: RadioSwatches.self, viewModelClass: RadioSwatchesModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Tags.self, viewModelClass: TagsModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Tag.self, viewModelClass: TagModel.self) - + MoleculeObjectMapping.shared()?.register(viewClass: Heart.self, viewModelClass: HeartModel.self) // MARK:- Other Atoms From 06df235fb293b059193b62c63793e0e1c611710f Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 8 Sep 2020 20:25:46 +0530 Subject: [PATCH 02/31] Code fixes and added accessibility. --- MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 48 +++++++++++++++---- .../Strings/en.lproj/Localizable.strings | 5 ++ .../Strings/es-MX.lproj/Localizable.strings | 4 ++ .../Strings/es.lproj/Localizable.strings | 6 +++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index 33625303..46435614 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -8,7 +8,8 @@ import UIKit -open class Heart: Control { +@objcMembers open class Heart: Control { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -16,6 +17,7 @@ open class Heart: Control { @objc public override var isSelected: Bool { didSet { heartModel?.isActive = isSelected + updateAccessibilityLabel() } } public var delegateObject: MVMCoreUIDelegateObject? @@ -24,9 +26,15 @@ open class Heart: Control { } //-------------------------------------------------- - // MARK: - Lifecycle + // MARK: - Constraints //-------------------------------------------------- - open override func draw(_ layer: CALayer, in ctx: CGContext) { + public var widthConstraint: NSLayoutConstraint? + public var heightConstraint: NSLayoutConstraint? + + //------------------------------------------------------ + // MARK: - State Handling + //------------------------------------------------------ + open override func draw(_ rect: CGRect) { //Draw the heart heartLayer?.removeFromSuperlayer() let heart = drawHeart() @@ -36,7 +44,7 @@ open class Heart: Control { func drawHeart() -> CAShapeLayer { let heart = CAShapeLayer() - let rect = CGRect(x: 0, y: 0, width: 22, height: 22) + let rect = CGRect(x: 0, y: 0, width: 10, height: 10) let leftArc = rect.width * 0.4 let rightArc = rect.height * 0.3 let arcRadius = sqrt(leftArc*leftArc + rightArc*rightArc)/2 @@ -60,15 +68,19 @@ open class Heart: Control { } //-------------------------------------------------- - // MARK: - MVMViewProtocol + // MARK: - Lifecycle //-------------------------------------------------- open override func setupView() { super.setupView() - } - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - layer.setNeedsDisplay() + addTarget(self, action: #selector(tapAction), for: .touchUpInside) + widthConstraint = widthAnchor.constraint(equalToConstant: 10) + widthConstraint?.isActive = true + heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1) + heightConstraint?.isActive = true + isAccessibilityElement = true + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "heart_action_hint") + accessibilityTraits = .button + updateAccessibilityLabel() } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { @@ -77,6 +89,22 @@ open class Heart: Control { guard let model = model as? HeartModel else { return } isSelected = model.isActive } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + /// Adjust accessibility label based on selection of Heart. + func updateAccessibilityLabel() { + if let message = MVMCoreUIUtility.hardcodedString(withKey: "heart"), + let selectedState = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_selected_state" : "heart_not_selected_state") { + accessibilityLabel = message + selectedState + } + } + + func tapAction() { + isSelected = !isSelected + setNeedsDisplay() + } } extension Int { diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index c280b737..2f8b642b 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -67,6 +67,11 @@ "AccOff" = "off"; "AccToggleHint" = "double tap to toggle"; +// MARK: Heart +"heart" = "Heart"; +"heart_action_hint" = "Double tap to select"; +"heart_selected_state" = "Selected"; +"heart_not_selected_state" = "Not Selected"; // MARK: Carousel "MVMCoreUIPageControl_currentpage_index" = "page %@ of %d"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings index f21df078..4917f6ee 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings @@ -49,6 +49,10 @@ "AccOn" = "encendido"; "AccOff" = "apagado"; "AccToggleHint" = "toca dos veces para alternar"; +// Heart +"heart_action_hint" = "Toca dos veces para seleccionar."; +"heart_selected_state" = "Seleccionado"; +"heart_not_selected_state" = "No Seleccionado"; // Carousel "MVMCoreUIPageControl_currentpage_index" = "página %@ de %d"; "MVMCoreUIPageControlslides_currentpage_index" = "diapositiva %@ of %d"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings index 47d124a9..5bf02ca0 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings @@ -51,6 +51,12 @@ "AccOn" = "encendido"; "AccOff" = "apagado"; "AccToggleHint" = "toca dos veces para alternar"; + +// Heart +"heart_action_hint" = "Toca dos veces para seleccionar."; +"heart_selected_state" = "Seleccionado"; +"heart_not_selected_state" = "No Seleccionado"; + // Carousel "MVMCoreUIPageControl_currentpage_index" = "página %@ de %d"; "MVMCoreUIPageControlslides_currentpage_index" = "diapositiva %@ of %d"; From 884f2ad69fac5f33ec1dd9a5f8cb41b39e3dd0d9 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Mon, 14 Sep 2020 17:38:20 +0530 Subject: [PATCH 03/31] Added action key. --- MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 4 ++-- MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index 46435614..5270fa62 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -44,7 +44,7 @@ import UIKit func drawHeart() -> CAShapeLayer { let heart = CAShapeLayer() - let rect = CGRect(x: 0, y: 0, width: 10, height: 10) + let rect = self.bounds let leftArc = rect.width * 0.4 let rightArc = rect.height * 0.3 let arcRadius = sqrt(leftArc*leftArc + rightArc*rightArc)/2 @@ -73,7 +73,7 @@ import UIKit open override func setupView() { super.setupView() addTarget(self, action: #selector(tapAction), for: .touchUpInside) - widthConstraint = widthAnchor.constraint(equalToConstant: 10) + widthConstraint = widthAnchor.constraint(equalToConstant: 22) widthConstraint?.isActive = true heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1) heightConstraint?.isActive = true diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift index 469dc4d9..3fe696f8 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift @@ -9,32 +9,32 @@ import Foundation open class HeartModel: MoleculeModelProtocol { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public static var identifier: String = "heart" public var backgroundColor: Color? public var isActive: Bool = false public var activeColor: Color = Color(uiColor: .mvmRed) public var inActiveColor: Color = Color(uiColor: .clear) + public var action: ActionModelProtocol? //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- - private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor case isActive case activeColor case inActiveColor + case action } //-------------------------------------------------- // MARK: - Codec //-------------------------------------------------- - required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) @@ -48,6 +48,7 @@ open class HeartModel: MoleculeModelProtocol { if let inActiveColor = try typeContainer.decodeIfPresent(Color.self, forKey: .inActiveColor) { self.inActiveColor = inActiveColor } + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public func encode(to encoder: Encoder) throws { @@ -57,5 +58,6 @@ open class HeartModel: MoleculeModelProtocol { try container.encodeIfPresent(isActive, forKey: .isActive) try container.encodeIfPresent(activeColor, forKey: .activeColor) try container.encodeIfPresent(inActiveColor, forKey: .inActiveColor) + try container.encodeModelIfPresent(action, forKey: .action) } } From 7c4c9ea43a6e09f4b56a3b53c6df8711af974681 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 29 Sep 2020 17:39:16 +0530 Subject: [PATCH 04/31] Code changes based on review comments and added default action noop. --- MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 7 ++++++- MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift | 12 +++++++----- .../Strings/en.lproj/Localizable.strings | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index 5270fa62..0cdc1f6d 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers open class Heart: Control { +@objcMembers open class Heart: Control, MFButtonProtocol { //-------------------------------------------------- // MARK: - Properties @@ -24,6 +24,7 @@ import UIKit public var heartModel: HeartModel? { return model as? HeartModel } + var additionalData: [AnyHashable: Any]? //-------------------------------------------------- // MARK: - Constraints @@ -86,6 +87,7 @@ import UIKit public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject + self.additionalData = additionalData guard let model = model as? HeartModel else { return } isSelected = model.isActive } @@ -103,6 +105,9 @@ import UIKit func tapAction() { isSelected = !isSelected + if let heartModel = heartModel { + Button.performButtonAction(with: heartModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: heartModel) + } setNeedsDisplay() } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift index 3fe696f8..b3d168ef 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift @@ -18,7 +18,7 @@ open class HeartModel: MoleculeModelProtocol { public var isActive: Bool = false public var activeColor: Color = Color(uiColor: .mvmRed) public var inActiveColor: Color = Color(uiColor: .clear) - public var action: ActionModelProtocol? + public var action: ActionModelProtocol = ActionNoopModel() //-------------------------------------------------- // MARK: - Keys @@ -48,16 +48,18 @@ open class HeartModel: MoleculeModelProtocol { if let inActiveColor = try typeContainer.decodeIfPresent(Color.self, forKey: .inActiveColor) { self.inActiveColor = inActiveColor } - action = try typeContainer.decodeModelIfPresent(codingKey: .action) + if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) { + self.action = action + } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(isActive, forKey: .isActive) - try container.encodeIfPresent(activeColor, forKey: .activeColor) - try container.encodeIfPresent(inActiveColor, forKey: .inActiveColor) + try container.encode(isActive, forKey: .isActive) + try container.encode(activeColor, forKey: .activeColor) + try container.encode(inActiveColor, forKey: .inActiveColor) try container.encodeModelIfPresent(action, forKey: .action) } } diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index 2f8b642b..8cf3e21e 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -68,7 +68,7 @@ "AccToggleHint" = "double tap to toggle"; // MARK: Heart -"heart" = "Heart"; +"heart" = "Favorite"; "heart_action_hint" = "Double tap to select"; "heart_selected_state" = "Selected"; "heart_not_selected_state" = "Not Selected"; From 223757ab0fae7b0dadf2418c919195a485a483ae Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Wed, 30 Sep 2020 12:16:04 +0530 Subject: [PATCH 05/31] Changed accessibility text for heart. --- MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 2 +- .../SupportingFiles/Strings/en.lproj/Localizable.strings | 6 +++--- .../SupportingFiles/Strings/es-MX.lproj/Localizable.strings | 6 +++--- .../SupportingFiles/Strings/es.lproj/Localizable.strings | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index 0cdc1f6d..eeda08af 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -45,7 +45,7 @@ import UIKit func drawHeart() -> CAShapeLayer { let heart = CAShapeLayer() - let rect = self.bounds + let rect = bounds let leftArc = rect.width * 0.4 let rightArc = rect.height * 0.3 let arcRadius = sqrt(leftArc*leftArc + rightArc*rightArc)/2 diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index 8cf3e21e..db90b9b7 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -69,9 +69,9 @@ // MARK: Heart "heart" = "Favorite"; -"heart_action_hint" = "Double tap to select"; -"heart_selected_state" = "Selected"; -"heart_not_selected_state" = "Not Selected"; +"heart_action_hint" = "Double tap to favorite"; +"heart_selected_state" = "Favorited"; +"heart_not_selected_state" = "Unfavorited"; // MARK: Carousel "MVMCoreUIPageControl_currentpage_index" = "page %@ of %d"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings index 4917f6ee..d6bfb89c 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings @@ -50,9 +50,9 @@ "AccOff" = "apagado"; "AccToggleHint" = "toca dos veces para alternar"; // Heart -"heart_action_hint" = "Toca dos veces para seleccionar."; -"heart_selected_state" = "Seleccionado"; -"heart_not_selected_state" = "No Seleccionado"; +"heart_action_hint" = "Toca dos veces en favorito"; +"heart_selected_state" = "Favoritos"; +"heart_not_selected_state" = "No favorito"; // Carousel "MVMCoreUIPageControl_currentpage_index" = "página %@ de %d"; "MVMCoreUIPageControlslides_currentpage_index" = "diapositiva %@ of %d"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings index 5bf02ca0..b06c972b 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings @@ -53,9 +53,9 @@ "AccToggleHint" = "toca dos veces para alternar"; // Heart -"heart_action_hint" = "Toca dos veces para seleccionar."; -"heart_selected_state" = "Seleccionado"; -"heart_not_selected_state" = "No Seleccionado"; +"heart_action_hint" = "Toca dos veces en favorito"; +"heart_selected_state" = "Favoritos"; +"heart_not_selected_state" = "No favorito"; // Carousel "MVMCoreUIPageControl_currentpage_index" = "página %@ de %d"; From 4862d3d07cf8932300c8cfb197ff088be33cb16d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 5 Oct 2020 14:03:35 -0400 Subject: [PATCH 06/31] Section List --- MVMCoreUI.xcodeproj/project.pbxproj | 48 +++++- MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 3 + .../ListDeviceComplexButtonMedium.swift | 2 +- .../Device/ListDeviceComplexButtonSmall.swift | 2 +- .../List/ListProgressBarThin.swift | 2 +- .../ListThreeColumnBillChanges.swift | 2 +- .../ListThreeColumnBillHistory.swift | 2 +- .../ListThreeColumnDataUsage.swift | 2 +- .../ListThreeColumnSpeedTest.swift | 2 +- .../ListTwoColumnCompareChanges.swift | 2 +- .../ListTwoColumnDropdownSelectors.swift | 2 +- .../TwoColumn/ListTwoColumnPriceDetails.swift | 2 +- .../ListFourColumnDataUsageDivider.swift | 2 +- ...ColumnFullWidthTextDividerSubsection.swift | 4 +- ...nFullWidthTextDividerSubsectionModel.swift | 8 +- ...ColumnTextWithWhitespaceDividerShort.swift | 4 +- ...nTextWithWhitespaceDividerShortModel.swift | 8 +- ...eColumnTextWithWhitespaceDividerTall.swift | 4 +- ...mnTextWithWhitespaceDividerTallModel.swift | 8 +- .../ListThreeColumnBillChangesDivider.swift | 2 +- .../ListThreeColumnBillHistoryDivider.swift | 2 +- .../ListThreeColumnDataUsageDivider.swift | 2 +- ...tThreeColumnInternationalDataDivider.swift | 2 +- .../ListThreeColumnPlanDataDivider.swift | 2 +- .../ListThreeColumnSpeedTestDivider.swift | 2 +- .../ListTwoColumnSubsectionDivider.swift | 2 +- .../{ => HeadersAndFooters}/FooterModel.swift | 0 .../{ => HeadersAndFooters}/FooterView.swift | 0 .../{ => HeadersAndFooters}/Header.swift | 0 .../{ => HeadersAndFooters}/HeaderModel.swift | 0 .../MoleculeHeaderModel.swift | 0 .../MoleculeHeaderView.swift | 0 .../MoleculeSectionFooter.swift | 57 ++++++++ .../MoleculeSectionFooterModel.swift | 31 ++++ .../MoleculeSectionHeader.swift | 72 +++++++++ .../MoleculeSectionHeaderModel.swift | 52 +++++++ .../Items/MoleculeCollectionViewCell.swift | 20 +-- .../Items/MoleculeListItemModel.swift | 2 +- .../Items/MoleculeTableViewCell.swift | 22 +-- .../LeftRightViews/CornerLabels.swift | 2 +- .../Templates/ListPageTemplateModel.swift | 10 ++ .../Templates/MoleculeListTemplate.swift | 59 ++++---- .../Templates/SectionListTemplate.swift | 137 ++++++++++++++++++ .../Templates/SectionListTemplateModel.swift | 91 ++++++++++++ .../BaseClasses/SectionHeaderFooterView.swift | 85 +++++++++++ ...iewControllerMappingObject+Extension.swift | 1 + 46 files changed, 650 insertions(+), 114 deletions(-) rename MVMCoreUI/Atomic/Molecules/{ => HeadersAndFooters}/FooterModel.swift (100%) rename MVMCoreUI/Atomic/Molecules/{ => HeadersAndFooters}/FooterView.swift (100%) rename MVMCoreUI/Atomic/Molecules/{ => HeadersAndFooters}/Header.swift (100%) rename MVMCoreUI/Atomic/Molecules/{ => HeadersAndFooters}/HeaderModel.swift (100%) rename MVMCoreUI/Atomic/Molecules/{ => HeadersAndFooters}/MoleculeHeaderModel.swift (100%) rename MVMCoreUI/Atomic/Molecules/{ => HeadersAndFooters}/MoleculeHeaderView.swift (100%) create mode 100644 MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift create mode 100644 MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooterModel.swift create mode 100644 MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift create mode 100644 MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeaderModel.swift create mode 100644 MVMCoreUI/Atomic/Templates/SectionListTemplate.swift create mode 100644 MVMCoreUI/Atomic/Templates/SectionListTemplateModel.swift create mode 100644 MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b67f6235..cbb26b62 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -305,6 +305,8 @@ D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; + D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2169300251E51E7002A6324 /* SectionListTemplate.swift */; }; + D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2169302251E53D9002A6324 /* SectionListTemplateModel.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */; }; D21B7F73243BAC6800051ABF /* CollectionItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */; }; @@ -464,6 +466,8 @@ D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */; }; D2CAC7D12511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */; }; D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */; }; + D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */; }; + D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */; }; D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; }; D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; }; D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; }; @@ -481,6 +485,9 @@ D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; }; D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; }; D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; }; + D2EC7BD52527B7A600F540AF /* MoleculeSectionHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */; }; + D2EC7BD92527B7CF00F540AF /* MoleculeSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */; }; + D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; }; DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; @@ -793,6 +800,8 @@ D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; + D2169300251E51E7002A6324 /* SectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplate.swift; sourceTree = ""; }; + D2169302251E53D9002A6324 /* SectionListTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplateModel.swift; sourceTree = ""; }; D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; }; D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionItemModelProtocol.swift; sourceTree = ""; }; @@ -954,6 +963,8 @@ D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationModel.swift; sourceTree = ""; }; D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertMainView+Extension.swift"; sourceTree = ""; }; D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertExpandableView+Extension.swift"; sourceTree = ""; }; + D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooterModel.swift; sourceTree = ""; }; + D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooter.swift; sourceTree = ""; }; D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = ""; }; D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = ""; }; D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = ""; }; @@ -970,6 +981,9 @@ D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = ""; }; D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = ""; }; D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = ""; }; + D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeaderModel.swift; sourceTree = ""; }; + D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeader.swift; sourceTree = ""; }; + D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderFooterView.swift; sourceTree = ""; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.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 = ""; }; @@ -1680,6 +1694,8 @@ D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */, D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */, D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */, + D2169302251E53D9002A6324 /* SectionListTemplateModel.swift */, + D2169300251E51E7002A6324 /* SectionListTemplate.swift */, ); path = Templates; sourceTree = ""; @@ -1707,6 +1723,7 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( + D2EC7BD22527A1E400F540AF /* HeadersAndFooters */, D2CAC7C9251104CB00C75681 /* TopNotification */, D2509ED42472EE0B001BFB9D /* NavigationBar */, D253BB9A24587023002DE544 /* OtherContainers */, @@ -1715,12 +1732,6 @@ D224798F2316A99F003FCCF9 /* LeftRightViews */, D224798E2316A995003FCCF9 /* HorizontalCombinationViews */, D224798D2316A988003FCCF9 /* VerticalCombinationViews */, - 01EB368C23609801006832FA /* HeaderModel.swift */, - D256E9922412880000360572 /* Header.swift */, - D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */, - D2A514662213885800345BFB /* MoleculeHeaderView.swift */, - 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, - D274CA322236A78900B01B62 /* FooterView.swift */, D260105723CF9CC500764D80 /* Doughnut */, ); path = Molecules; @@ -2022,6 +2033,7 @@ 0A5D59C323AD488600EFD9E9 /* Protocols */, 0A7918F423F5E7EA00772FF4 /* ImageView.swift */, D272F5F82473163100BD1A8F /* BarButtonItem.swift */, + D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */, ); path = BaseClasses; sourceTree = ""; @@ -2085,6 +2097,23 @@ path = TopNotification; sourceTree = ""; }; + D2EC7BD22527A1E400F540AF /* HeadersAndFooters */ = { + isa = PBXGroup; + children = ( + 01EB368C23609801006832FA /* HeaderModel.swift */, + D256E9922412880000360572 /* Header.swift */, + D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */, + D2A514662213885800345BFB /* MoleculeHeaderView.swift */, + 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, + D274CA322236A78900B01B62 /* FooterView.swift */, + D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */, + D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */, + D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */, + D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */, + ); + path = HeadersAndFooters; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2238,6 +2267,7 @@ D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */, D28BA7452481652D00B75CB8 /* TabBarProtocol.swift in Sources */, AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */, + D2EC7BD92527B7CF00F540AF /* MoleculeSectionHeader.swift in Sources */, D28764AA2458980300CB882D /* ThreeLayerFillMiddleTemplate.swift in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonSelectionHelper.swift in Sources */, D2092353244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift in Sources */, @@ -2253,6 +2283,7 @@ AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */, AA7F32AD246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift in Sources */, D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */, + D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */, 0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, @@ -2406,6 +2437,7 @@ D260105323CEA61600764D80 /* ToggleModel.swift in Sources */, 014AA72523C501E2006F3E93 /* ContainerModel.swift in Sources */, 0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */, + D2EC7BD52527B7A600F540AF /* MoleculeSectionHeaderModel.swift in Sources */, AAA905DF24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, @@ -2418,6 +2450,7 @@ D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */, 0A775F2824893937009EFB58 /* ThreeHeadlineBodyLinkModel.swift in Sources */, 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */, + D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */, D23EA802247EBED400D60C34 /* ImageBarButtonItem.swift in Sources */, AA45AA0B24BF0263007A6EA7 /* LockUpsPlanNamesModel.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, @@ -2522,6 +2555,7 @@ D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */, 0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */, + D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */, 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */, BB105859248DEFF70069D008 /* UICollectionViewLeftAlignedLayout.swift in Sources */, D253BB9C245874F8002DE544 /* BGImageMolecule.swift in Sources */, @@ -2604,6 +2638,7 @@ AA104B1A24474A66004D2810 /* HeadersH2Buttons.swift in Sources */, C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, + D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */, BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */, 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */, 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */, @@ -2645,6 +2680,7 @@ AA0A257A24766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift in Sources */, 011D95AD2406BB57000E3791 /* FormHolderProtocol.swift in Sources */, 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */, + D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */, 0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */, D264FAA7243FE13B00D98315 /* RadioBox.swift in Sources */, ); diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index a598b113..96506df0 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -149,6 +149,9 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ModuleMolecule.self, viewModelClass: ModuleMoleculeModel.self) MoleculeObjectMapping.shared()?.register(viewClass: BGImageMolecule.self, viewModelClass: BGImageMoleculeModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionHeader.self, viewModelClass: MoleculeSectionHeaderModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionFooter.self, viewModelClass: MoleculeSectionFooterModel.self) + // MARK:- Other Molecules MoleculeObjectMapping.shared()?.register(viewClass: DoughnutChartView.self, viewModelClass: DoughnutChartModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift index 986ee73b..7a9e8b7e 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift @@ -77,7 +77,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift index cd43215b..dc086400 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift @@ -77,7 +77,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift index 9f469d28..79530eb6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift @@ -94,7 +94,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift index c9368057..3db13a01 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift @@ -59,7 +59,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift index 98ec315a..b80c1ac6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift @@ -55,7 +55,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift index 24da6877..5ebaf0f6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift @@ -58,7 +58,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift index ba7c8794..64a7cd9f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift @@ -64,7 +64,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift index 10c4511f..9f7895af 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift @@ -90,7 +90,7 @@ rightHeadlineBodyLink.body.setFontStyle(.RegularBodySmall) } - public override class func estimatedHeight(with molecule: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(with molecule: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift index 6b77e709..eb8fb0ea 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift @@ -54,7 +54,7 @@ rightDropDown.set(with:model.rightDropDown, delegateObject, additionalData) } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift index 95b512b0..3e67b86f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift @@ -59,7 +59,7 @@ rightLabel.setFontStyle(.RegularBodySmall) } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 15 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift index 96c72865..f97fea4a 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift @@ -66,7 +66,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift index 3ed2065e..3f48c3d8 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift @@ -53,9 +53,7 @@ import Foundation super.set(with: model, delegateObject, additionalData) guard let model = model as? ListOneColumnFullWidthTextDividerSubsectionModel else { return } - - headline.set(with: model.headline, delegateObject, additionalData) - body.set(with: model.body, delegateObject, additionalData) + stack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, additionalData) updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift index 063c8cfa..5040fbd4 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift @@ -16,13 +16,13 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo public static var identifier: String = "list1CTxtDiv3" public var headline: LabelModel - public var body: LabelModel + public var body: LabelModel? //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(headline: LabelModel, body: LabelModel) { + public init(headline: LabelModel, body: LabelModel?) { self.headline = headline self.body = body super.init() @@ -54,7 +54,7 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) headline = try typeContainer.decode(LabelModel.self, forKey: .headline) - body = try typeContainer.decode(LabelModel.self, forKey: .body) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) try super.init(from: decoder) } @@ -63,6 +63,6 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(headline, forKey: .headline) - try container.encode(body, forKey: .body) + try container.encodeIfPresent(body, forKey: .body) } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift index 02d320a9..2f023b78 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift @@ -53,9 +53,7 @@ import Foundation super.set(with: model, delegateObject, additionalData) guard let model = model as? ListOneColumnTextWithWhitespaceDividerShortModel else { return } - - headline.set(with: model.headline, delegateObject, additionalData) - body.set(with: model.body, delegateObject, additionalData) + stack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, additionalData) updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift index f6303a85..66025760 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift @@ -16,13 +16,13 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo public static var identifier: String = "list1CTxtDiv1" public var headline: LabelModel - public var body: LabelModel + public var body: LabelModel? //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(headline: LabelModel, body: LabelModel) { + public init(headline: LabelModel, body: LabelModel?) { self.headline = headline self.body = body super.init() @@ -54,7 +54,7 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) headline = try typeContainer.decode(LabelModel.self, forKey: .headline) - body = try typeContainer.decode(LabelModel.self, forKey: .body) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) try super.init(from: decoder) } @@ -63,6 +63,6 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(headline, forKey: .headline) - try container.encode(body, forKey: .body) + try container.encodeIfPresent(body, forKey: .body) } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift index 704057db..62ee43f5 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift @@ -52,9 +52,7 @@ import Foundation super.set(with: model, delegateObject, additionalData) guard let model = model as? ListOneColumnTextWithWhitespaceDividerTallModel else { return } - - headline.set(with: model.headline, delegateObject, additionalData) - body.set(with: model.body, delegateObject, additionalData) + stack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, additionalData) updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift index e3bb634d..c713b0d2 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift @@ -16,13 +16,13 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol public static var identifier: String = "list1CTxtDiv2" public var headline: LabelModel - public var body: LabelModel + public var body: LabelModel? //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(headline: LabelModel, body: LabelModel) { + public init(headline: LabelModel, body: LabelModel?) { self.headline = headline self.body = body super.init() @@ -54,7 +54,7 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) headline = try typeContainer.decode(LabelModel.self, forKey: .headline) - body = try typeContainer.decode(LabelModel.self, forKey: .body) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) try super.init(from: decoder) } @@ -63,6 +63,6 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(headline, forKey: .headline) - try container.encode(body, forKey: .body) + try container.encodeIfPresent(body, forKey: .body) } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift index c4a42835..32425fca 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift @@ -62,7 +62,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift index 1246e98c..64830441 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift @@ -49,7 +49,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift index 7fe35b92..dd2ecc03 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift @@ -61,7 +61,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift index 1d842266..51913799 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift @@ -60,7 +60,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift index 9e5c2f05..c75c77df 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift @@ -62,7 +62,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift index 91a86643..c23a5cdc 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift @@ -61,7 +61,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift index b3972ae6..ddcc27c1 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift @@ -58,7 +58,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/FooterModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/FooterModel.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/FooterView.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterView.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/FooterView.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterView.swift diff --git a/MVMCoreUI/Atomic/Molecules/Header.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/Header.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift diff --git a/MVMCoreUI/Atomic/Molecules/HeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/HeaderModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/HeaderModel.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/HeaderModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/MoleculeHeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/MoleculeHeaderModel.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/MoleculeHeaderView.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/MoleculeHeaderView.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift new file mode 100644 index 00000000..2e2f48b2 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift @@ -0,0 +1,57 @@ +// +// MoleculeSectionFooter.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/5/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionFooter: SectionHeaderFooterView { + open var molecule: MoleculeViewProtocol? + public let containerHelper = ContainerHelper() + + /// Adds the molecule to the view. + open func addMolecule(_ molecule: MoleculeViewProtocol) { + contentView.addSubview(molecule) + containerHelper.constrainView(molecule) + molecule.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) + self.molecule = molecule + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + containerHelper.updateViewMargins(self, model: model as? MoleculeSectionFooterModel, size: size) + contentView.directionalLayoutMargins = directionalLayoutMargins + (molecule as? MVMCoreViewProtocol)?.updateView(size) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? MoleculeSectionFooterModel else { return } + if molecule != nil { + molecule?.set(with: model.molecule, delegateObject, additionalData) + } else if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: additionalData) { + addMolecule(moleculeView) + } + containerHelper.set(with: model, for: molecule as? MVMCoreUIViewConstrainingProtocol) + } + + open override func reset() { + super.reset() + molecule?.reset() + } + + public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return MoleculeContainer.nameForReuse(with: model, delegateObject) + } + + public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) + } + + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return MoleculeContainer.estimatedHeight(with: model, delegateObject) ?? 80.0 + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooterModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooterModel.swift new file mode 100644 index 00000000..edf9fc7a --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooterModel.swift @@ -0,0 +1,31 @@ +// +// MoleculeSectionFooterModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/5/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionFooterModel: MoleculeContainerModel, SectionListHeaderFooterModel { + public class override var identifier: String { + return "sectionFooter" + } + + /// Defaults to set + public override func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topPadding == nil { + topPadding = PaddingDefaultVerticalSpacing3 + } + if bottomPadding == nil { + bottomPadding = PaddingDefaultVerticalSpacing3 + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift new file mode 100644 index 00000000..a8623517 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift @@ -0,0 +1,72 @@ +// +// MoleculeSectionHeader.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionHeader: SectionHeaderFooterView { + open var molecule: MoleculeViewProtocol? + public let containerHelper = ContainerHelper() + public let line = Line() + + /// Adds the molecule to the view. + open func addMolecule(_ molecule: MoleculeViewProtocol) { + contentView.addSubview(molecule) + containerHelper.constrainView(molecule) + molecule.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) + self.molecule = molecule + } + + public override func setupView() { + super.setupView() + line.setStyle(.thin) + contentView.addSubview(line) + NSLayoutConstraint.pinViewBottom(toSuperview: line, useMargins: false, constant: 0).isActive = true + NSLayoutConstraint.pinViewLeft(toSuperview: line, useMargins: true, constant: 0).isActive = true + NSLayoutConstraint.pinViewRight(toSuperview: line, useMargins: true, constant: 0).isActive = true + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + containerHelper.updateViewMargins(self, model: model as? MoleculeSectionHeaderModel, size: size) + contentView.directionalLayoutMargins = directionalLayoutMargins + (molecule as? MVMCoreViewProtocol)?.updateView(size) + line.updateView(size) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? MoleculeSectionHeaderModel else { return } + if molecule != nil { + molecule?.set(with: model.molecule, delegateObject, additionalData) + } else if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: additionalData) { + addMolecule(moleculeView) + } + containerHelper.set(with: model, for: molecule as? MVMCoreUIViewConstrainingProtocol) + if let lineModel = model.line { + line.set(with: lineModel, delegateObject, additionalData) + } + } + + open override func reset() { + super.reset() + line.setStyle(.thin) + molecule?.reset() + } + + public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return MoleculeContainer.nameForReuse(with: model, delegateObject) + } + + public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) + } + + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return MoleculeContainer.estimatedHeight(with: model, delegateObject) ?? 80.0 + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeaderModel.swift new file mode 100644 index 00000000..ef6b057e --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeaderModel.swift @@ -0,0 +1,52 @@ +// +// MoleculeSectionHeaderModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionHeaderModel: MoleculeContainerModel, SectionListHeaderFooterModel { + public class override var identifier: String { + return "sectionHeader" + } + public var line: LineModel? + + private enum CodingKeys: String, CodingKey { + case line + case backgroundColor + } + + /// Defaults to set + public override func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topPadding == nil { + topPadding = PaddingDefaultVerticalSpacing3 + } + if bottomPadding == nil { + bottomPadding = PaddingDefaultVerticalSpacing3 + } + if line == nil { + line = LineModel(type: .thin) + } + } + + required public init(from decoder: Decoder) throws { + try super.init(from: decoder) + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(line, forKey: .line) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift index bf3714bd..f6fe580f 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift @@ -34,26 +34,14 @@ open class MoleculeCollectionViewCell: CollectionViewCell { } open override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let moleculeModel = (model as? MoleculeCollectionItemModel)?.molecule else { return "\(self)<>" } - let className = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - let moleculeName = className?.nameForReuse(with: moleculeModel, delegateObject) ?? moleculeModel.moleculeName - return "\(self)<\(moleculeName)>" + return MoleculeContainer.nameForReuse(with: model, delegateObject) } open override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - guard let moleculeModel = (model as? MoleculeCollectionItemModel)?.molecule, - let theClass = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - else { return nil } - - return theClass.requiredModules(with: moleculeModel, delegateObject, error: error) + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let model = model as? MoleculeCollectionItemModel, - let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(model.molecule), - let height = classType.estimatedHeight(with: model.molecule, delegateObject) - else { return 100 } - - return height + (model.topPadding ?? 0) + (model.bottomPadding ?? 0) + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return MoleculeContainer.estimatedHeight(with: model, delegateObject) ?? 100 } } diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift index bee9d612..1028362e 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift @@ -10,7 +10,7 @@ import Foundation import MVMCore -@objcMembers public class MoleculeListItemModel: ListItemModel, MoleculeModelProtocol { +@objcMembers public class MoleculeListItemModel: ListItemModel, MoleculeContainerModelProtocol, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift index 532c21f4..92d04bed 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift @@ -30,28 +30,16 @@ import UIKit } public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule else { return "\(self)<>" } - - let className = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - let moleculeName = className?.nameForReuse(with: moleculeModel, delegateObject) ?? moleculeModel.moleculeName - - return "\(self)<\(moleculeName)>" + return MoleculeContainer.nameForReuse(with: model, delegateObject) } public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule, - let theClass = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - else { return nil } - - return theClass.requiredModules(with: moleculeModel, delegateObject, error: error) + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) } - public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule, - let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel), - let height = classType.estimatedHeight(with: moleculeModel, delegateObject) + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let height = MoleculeContainer.estimatedHeight(with: model, delegateObject) else { return 80 } - - return max(2 * PaddingDefaultVerticalSpacing3, height) + return height } } diff --git a/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift index be3ac62e..3f8ff8a7 100644 --- a/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift @@ -157,7 +157,7 @@ import UIKit bottomRightLabel.setFontStyle(.RegularMicro) } - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 34 } diff --git a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift index b2b52573..14be7809 100644 --- a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift @@ -18,6 +18,15 @@ import Foundation } public var molecules: [ListItemModelProtocol & MoleculeModelProtocol]? public var line: LineModel? + + /// This template requires content. + func validateModelHasContent() throws { + if header == nil, + molecules?.count ?? 0 == 0, + footer == nil { + throw ModelRegistry.Error.decoderOther(message: "List template requires atleast one of the following: header, footer, molecules") + } + } //-------------------------------------------------- // MARK: - Initializer @@ -47,6 +56,7 @@ import Foundation molecules = try typeContainer.decodeModelsIfPresent(codingKey: .molecules) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) try super.init(from: decoder) + try validateModelHasContent() } open override func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 372ae778..70416491 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -14,24 +14,15 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol // MARK: - Stored Properties //-------------------------------------------------- - public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: (ListItemModelProtocol & MoleculeModelProtocol))]? + public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)]? var observer: NSKeyValueObservation? public var templateModel: ListPageTemplateModel? - + //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- - open override func parsePageJSON() throws { - try parseTemplate(json: loadObject?.pageJSON) - try super.parsePageJSON() - } - - // For subclassing the model. - open func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> ListPageTemplateModel { - return try decoder.decode(ListPageTemplateModel.self, from: data) - } open override var loadObject: MVMCoreLoadObject? { didSet { @@ -50,6 +41,16 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- + + open override func parsePageJSON() throws { + try parseTemplate(json: loadObject?.pageJSON) + try super.parsePageJSON() + } + + // For subclassing the model. + open func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> ListPageTemplateModel { + return try decoder.decode(ListPageTemplateModel.self, from: data) + } open override func viewForTop() -> UIView { guard let headerModel = templateModel?.header, @@ -72,21 +73,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return molecule } - open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { - - guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false } - - // This template requires atleast one of the three layers. - if templateModel?.header == nil, - templateModel?.molecules?.count ?? 0 == 0, - templateModel?.footer == nil, - let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), messageToLog: "List template requires atleast one of the following: header, footer, molecules", code: CoreUIErrorCode.ErrorCodeListMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) { - error.pointee = errorObject - return false - } - return true - } - open override func handleNewData() { topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false @@ -109,7 +95,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - guard let moleculeInfo = moleculesInfo?[indexPath.row], + guard let moleculeInfo = getMoleculeInfo(for: indexPath), let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) else { return 0 } @@ -122,7 +108,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let moleculeInfo = moleculesInfo?[indexPath.row], + guard let moleculeInfo = getMoleculeInfo(for: indexPath), let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) else { return UITableViewCell() } @@ -175,7 +161,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol var indexPaths: [IndexPath] = [] for molecule in molecules { - if let info = self.getMoleculeInfo(with: molecule) { + if let info = self.createMoleculeInfo(with: molecule) { self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) let index = indexPath.row + 1 + indexPaths.count self.moleculesInfo?.insert(info, at: index) @@ -211,8 +197,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol // MARK: - Convenience //-------------------------------------------------- - /// Returns the (identifier, class) of the molecule for the given map. - func getMoleculeInfo(with listItem: (ListItemModelProtocol & MoleculeModelProtocol)?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol & MoleculeModelProtocol)? { + /// Returns the (identifier, class) of the molecule for the given model. + func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { guard let listItem = listItem, let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) @@ -223,14 +209,19 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return (moleculeName, moleculeClass, listItem) } + /// Returns the (identifier, class) of the molecule for the indexPath. + func getMoleculeInfo(for indexPath: IndexPath) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { + return moleculesInfo?[indexPath.row] + } + /// Sets up the molecule list and ensures no errors loading all content. - func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: (ListItemModelProtocol & MoleculeModelProtocol))]? { + func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)]? { - var moleculeList: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol & MoleculeModelProtocol)] = [] + var moleculeList: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)] = [] if let molecules = templateModel?.molecules { for molecule in molecules { - if let info = getMoleculeInfo(with: molecule) { + if let info = createMoleculeInfo(with: molecule) { moleculeList.append(info) } } diff --git a/MVMCoreUI/Atomic/Templates/SectionListTemplate.swift b/MVMCoreUI/Atomic/Templates/SectionListTemplate.swift new file mode 100644 index 00000000..bc12146f --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/SectionListTemplate.swift @@ -0,0 +1,137 @@ +// +// SectionListTemplate.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/25/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class SectionListTemplate: MoleculeListTemplate { + + public var sectionMoleculesInfo: [(header: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, footer: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, rows: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)])]? + + open override func viewDidLoad() { + super.viewDidLoad() + tableView.sectionHeaderHeight = UITableView.automaticDimension + } + + override func setup() { + // Create quick reference + var sectionList: [(header: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, footer: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, rows: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)])] = [] + + if let sections = (templateModel as? SectionListTemplateModel)?.sections { + for section in sections { + let header = createMoleculeInfo(with: section.header) + let footer = createMoleculeInfo(with: section.footer) + var rows: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)] = [] + for row in section.rows { + if let rowInfo = createMoleculeInfo(with: row) { + rows.append(rowInfo) + } + } + sectionList.append((header: header, footer: footer, rows: rows)) + } + } + + sectionMoleculesInfo = sectionList.count > 0 ? sectionList : nil + } + + override func getMoleculeInfo(for indexPath: IndexPath) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { + return sectionMoleculesInfo?[indexPath.section].rows[indexPath.row] + } + + open override func createTableView() -> TableView { + let tableView = TableView(frame: .zero, style: .plain) + tableView.backgroundColor = .clear + tableView.separatorStyle = UITableViewCell.SeparatorStyle.none + tableView.delegate = self + tableView.dataSource = self + tableView.insetsContentViewsToSafeArea = false + return tableView + } + + // For subclassing the model. + open override func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> SectionListTemplateModel { + return try decoder.decode(SectionListTemplateModel.self, from: data) + } + + open override func registerWithTable() { + super.registerWithTable() + guard let sections = sectionMoleculesInfo else { return } + for section in sections { + if let header = section.header { + tableView.register(header.class, forHeaderFooterViewReuseIdentifier: header.identifier) + } + if let footer = section.footer { + tableView.register(footer.class, forHeaderFooterViewReuseIdentifier: footer.identifier) + } + for row in section.rows { + tableView.register(row.class, forCellReuseIdentifier: row.identifier) + } + } + } + + public func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat { + guard let moleculeInfo = sectionMoleculesInfo?[section].header else { + return CGFloat.leastNormalMagnitude + } + let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) + return estimatedHeight ?? 80 + } + + public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + guard (sectionMoleculesInfo?[section].header) != nil else { + return CGFloat.leastNormalMagnitude + } + return UITableView.automaticDimension + } + + public func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat { + guard let moleculeInfo = sectionMoleculesInfo?[section].footer else { + return CGFloat.leastNormalMagnitude + } + let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) + return estimatedHeight ?? 80 + } + + public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + guard (sectionMoleculesInfo?[section].footer) != nil else { + return CGFloat.leastNormalMagnitude + } + return UITableView.automaticDimension + } + + public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let sectionInfo = sectionMoleculesInfo?[section], + let headerInfo = sectionInfo.header else { return nil } + let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerInfo.identifier) + (header as? MoleculeViewProtocol)?.reset() + (header as? MoleculeViewProtocol)?.set(with: headerInfo.molecule, delegateObjectIVar, nil) + (header as? MVMCoreViewProtocol)?.updateView(tableView.bounds.width) + // Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells + header?.layoutIfNeeded() + return header + } + + public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + guard let sectionInfo = sectionMoleculesInfo?[section], + let footerInfo = sectionInfo.footer else { return nil } + let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: footerInfo.identifier) + (footer as? MoleculeViewProtocol)?.reset() + (footer as? MoleculeViewProtocol)?.set(with: footerInfo.molecule, delegateObjectIVar, nil) + (footer as? MVMCoreViewProtocol)?.updateView(tableView.bounds.width) + // Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells + footer?.layoutIfNeeded() + return footer + } + + open override func getNumberOfSections() -> Int { + return sectionMoleculesInfo?.count ?? 0 + } + + open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sectionMoleculesInfo?[section].rows.count ?? 0 + } +} diff --git a/MVMCoreUI/Atomic/Templates/SectionListTemplateModel.swift b/MVMCoreUI/Atomic/Templates/SectionListTemplateModel.swift new file mode 100644 index 00000000..280ea4e5 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/SectionListTemplateModel.swift @@ -0,0 +1,91 @@ +// +// SectionListTemplateModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/25/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol SectionListHeaderFooterModel { + +} + +@objcMembers open class SectionModel: Codable { + public var header: (SectionListHeaderFooterModel & MoleculeModelProtocol)? + public var footer: (SectionListHeaderFooterModel & MoleculeModelProtocol)? + public var rows: [ListItemModelProtocol & MoleculeModelProtocol] + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case header + case footer + case rows + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + header = try typeContainer.decodeModelIfPresent(codingKey: .header) + footer = try typeContainer.decodeModelIfPresent(codingKey: .footer) + rows = try typeContainer.decodeModels(codingKey: .rows) + } + + open func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModelIfPresent(header, forKey: .header) + try container.encodeModelIfPresent(footer, forKey: .footer) + try container.encodeModels(rows, forKey: .rows) + } +} + +@objcMembers open class SectionListTemplateModel: ListPageTemplateModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + open override class var identifier: String { + return "sectionList" + } + + public var sections: [SectionModel] + + override func validateModelHasContent() throws { + if header == nil, + sections.count == 0, + footer == nil { + throw ModelRegistry.Error.decoderOther(message: "Section List template requires atleast one of the following: header, footer, sections") + } + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case sections + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + sections = try typeContainer.decode([SectionModel].self, forKey: .sections) + try super.init(from: decoder) + } + + open override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(sections, forKey: .sections) + } +} diff --git a/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift b/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift new file mode 100644 index 00000000..b4b7f8eb --- /dev/null +++ b/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift @@ -0,0 +1,85 @@ +// +// SectionHeaderFooterView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class SectionHeaderFooterView: UITableViewHeaderFooterView, MoleculeViewProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + open var model: MoleculeModelProtocol? + + private var initialSetupPerformed = false + + //-------------------------------------------------- + // MARK: - Initialization + //-------------------------------------------------- + + public override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + public func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() + } + } + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.model = model + if let backgroundColor = model.backgroundColor { + contentView.backgroundColor = backgroundColor.uiColor + } + } + + open func reset() { + contentView.backgroundColor = .white + } + + // MARK: Overridables + // Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead. + open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return model.moleculeName + } + + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return nil + } + + open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + return nil + } +} + +// MARK:- MVMCoreViewProtocol +extension SectionHeaderFooterView: MVMCoreViewProtocol { + + open func updateView(_ size: CGFloat) { } + + /// Will be called only once. + open func setupView() { + translatesAutoresizingMaskIntoConstraints = false + insetsLayoutMarginsFromSafeArea = false + preservesSuperviewLayoutMargins = false + contentView.insetsLayoutMarginsFromSafeArea = false + contentView.preservesSuperviewLayoutMargins = false + MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0) + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift index 36f13be0..efd70570 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift @@ -20,6 +20,7 @@ public extension MVMCoreUIViewControllerMappingObject { register(template: MoleculeListTemplate.self) add(toTemplateViewControllerMapping: ["modalList": MVMCoreViewControllerProgrammaticMappingObject(with: ModalMoleculeListTemplate.self)!]) + add(toTemplateViewControllerMapping: [SectionListTemplateModel.identifier: MVMCoreViewControllerProgrammaticMappingObject(with: SectionListTemplate.self)!]) register(template: ThreeLayerTemplate.self) register(template: ThreeLayerCenterTemplate.self) From f32232a50cbe76dbb07f29e34bff2927e6ca6031 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 5 Oct 2020 14:27:45 -0400 Subject: [PATCH 07/31] remove test code --- .../Molecules/HeadersAndFooters/MoleculeSectionFooter.swift | 1 - .../Molecules/HeadersAndFooters/MoleculeSectionHeader.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift index 2e2f48b2..33caba83 100644 --- a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift @@ -16,7 +16,6 @@ import Foundation open func addMolecule(_ molecule: MoleculeViewProtocol) { contentView.addSubview(molecule) containerHelper.constrainView(molecule) - molecule.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) self.molecule = molecule } diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift index a8623517..092c2f82 100644 --- a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift @@ -17,7 +17,6 @@ import Foundation open func addMolecule(_ molecule: MoleculeViewProtocol) { contentView.addSubview(molecule) containerHelper.constrainView(molecule) - molecule.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) self.molecule = molecule } From d7b39a8d0d4a86715bda696287f4ec1ebf6e9e2a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 5 Oct 2020 14:37:13 -0400 Subject: [PATCH 08/31] clean up header --- .../HeadersAndFooters/MoleculeSectionHeader.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift index 092c2f82..5c805a33 100644 --- a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift @@ -24,9 +24,7 @@ import Foundation super.setupView() line.setStyle(.thin) contentView.addSubview(line) - NSLayoutConstraint.pinViewBottom(toSuperview: line, useMargins: false, constant: 0).isActive = true - NSLayoutConstraint.pinViewLeft(toSuperview: line, useMargins: true, constant: 0).isActive = true - NSLayoutConstraint.pinViewRight(toSuperview: line, useMargins: true, constant: 0).isActive = true + NSLayoutConstraint.constraintPinSubview(line, pinTop: false, topConstant: 0, pinBottom: true, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0) } open override func updateView(_ size: CGFloat) { @@ -46,9 +44,7 @@ import Foundation addMolecule(moleculeView) } containerHelper.set(with: model, for: molecule as? MVMCoreUIViewConstrainingProtocol) - if let lineModel = model.line { - line.set(with: lineModel, delegateObject, additionalData) - } + line.setOptional(with: model.line, delegateObject, additionalData) } open override func reset() { From 8b09f8f2bdfc41d264b350337070bf8f47f90a15 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 5 Oct 2020 14:39:58 -0400 Subject: [PATCH 09/31] fix miss clean --- .../Molecules/HeadersAndFooters/MoleculeSectionHeader.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift index 5c805a33..16aaacfe 100644 --- a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift @@ -24,7 +24,9 @@ import Foundation super.setupView() line.setStyle(.thin) contentView.addSubview(line) - NSLayoutConstraint.constraintPinSubview(line, pinTop: false, topConstant: 0, pinBottom: true, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0) + NSLayoutConstraint.pinViewBottom(toSuperview: line, useMargins: false, constant: 0).isActive = true + NSLayoutConstraint.pinViewLeft(toSuperview: line, useMargins: true, constant: 0).isActive = true + NSLayoutConstraint.pinViewRight(toSuperview: line, useMargins: true, constant: 0).isActive = true } open override func updateView(_ size: CGFloat) { From 2ef34939b7e0c1dd59390843f2bde08441d7fe33 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 5 Oct 2020 15:21:52 -0400 Subject: [PATCH 10/31] underline enhancement --- .../Atomic/Atoms/Views/Label/Label.swift | 5 +- .../Label/LabelAttributeUnderlineModel.swift | 96 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 73de7760..ef3fe04f 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -297,8 +297,9 @@ public typealias ActionBlock = () -> () for attribute in attributes { let range = NSRange(location: attribute.location, length: attribute.length) switch attribute { - case _ as LabelAttributeUnderlineModel: - attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) + case let underlineAtt as LabelAttributeUnderlineModel: + attributedString.addAttribute(.underlineStyle, value: underlineAtt.underlineValue.rawValue, range: range) + attributedString.addAttribute(.underlineColor, value: underlineAtt.color.uiColor, range: range) case _ as LabelAttributeStrikeThroughModel: attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift index d052686d..4dbdab9b 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift @@ -8,16 +8,112 @@ import UIKit + @objcMembers public class LabelAttributeUnderlineModel: LabelAttributeModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + override public class var identifier: String { return "underline" } + var underlineValue: NSUnderlineStyle { + + if let pattern = pattern?.value() { + return NSUnderlineStyle(rawValue: style.value() | pattern) + } else { + return NSUnderlineStyle(rawValue: style.value()) + } + } + + var color: Color = Color(uiColor: .mvmBlack) + var style: UnderlineStyle = .single + var pattern: UnderlineStyle.Pattern? + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case color + case style + case pattern + } + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) { + self.color = color + } + + if let style = try typeContainer.decodeIfPresent(UnderlineStyle.self, forKey: .style) { + self.style = style + } + + if let pattern = try typeContainer.decodeIfPresent(UnderlineStyle.Pattern.self, forKey: .pattern) { + self.pattern = pattern + } + 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.encode(color, forKey: .color) + try container.encode(style, forKey: .style) + try container.encodeIfPresent(pattern, forKey: .pattern) + } +} + +public enum UnderlineStyle: String, Codable { + case none + case single + case thick + case double + + func value() -> Int { + switch self { + case .none: + return 0 + + case .single: + return NSUnderlineStyle.single.rawValue + + case .thick: + return NSUnderlineStyle.thick.rawValue + + case .double: + return NSUnderlineStyle.double.rawValue + } + } + + public enum Pattern: String, Codable { + case dot + case dash + case dashDot + case dashDotDot + case byWord + + func value() -> Int { + switch self { + case .dot: + return NSUnderlineStyle.patternDot.rawValue + + case .dash: + return NSUnderlineStyle.patternDash.rawValue + + case .dashDot: + return NSUnderlineStyle.patternDashDot.rawValue + + case .dashDotDot: + return NSUnderlineStyle.patternDashDotDot.rawValue + + case .byWord: + return NSUnderlineStyle.byWord.rawValue + } + } } } From 229bc94568cc35d66fe0455a2183a189035371d3 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 5 Oct 2020 15:37:19 -0400 Subject: [PATCH 11/31] comment --- .../Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift index 4dbdab9b..381f4eff 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift @@ -18,6 +18,8 @@ import UIKit return "underline" } + /// This returns the NSUnderlineStyle used in NSAttributedValue. If there is a pattern, it will return + /// a new NSUnderlineStyle derived from the bitmask of style | pattern. var underlineValue: NSUnderlineStyle { if let pattern = pattern?.value() { From 7643b86b51cbd3f030e815c8f6a25a62de7e94e8 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 5 Oct 2020 18:03:35 -0400 Subject: [PATCH 12/31] allow for textColor first. --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 4 +++- .../Atoms/Views/Label/LabelAttributeUnderlineModel.swift | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index eab432b2..c03d9a84 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -324,7 +324,9 @@ public typealias ActionBlock = () -> () switch attribute { case let underlineAtt as LabelAttributeUnderlineModel: attributedString.addAttribute(.underlineStyle, value: underlineAtt.underlineValue.rawValue, range: range) - attributedString.addAttribute(.underlineColor, value: underlineAtt.color.uiColor, range: range) + if let underlineColor = underlineAtt.color?.uiColor { + attributedString.addAttribute(.underlineColor, value: underlineColor, range: range) + } case _ as LabelAttributeStrikeThroughModel: attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift index 381f4eff..a3adbe00 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift @@ -29,7 +29,7 @@ import UIKit } } - var color: Color = Color(uiColor: .mvmBlack) + var color: Color? var style: UnderlineStyle = .single var pattern: UnderlineStyle.Pattern? @@ -64,7 +64,7 @@ import UIKit public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(color, forKey: .color) + try container.encodeIfPresent(color, forKey: .color) try container.encode(style, forKey: .style) try container.encodeIfPresent(pattern, forKey: .pattern) } From 1bf75c99292bd8472b06ac445657f1b48f7a5c5a Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 6 Oct 2020 16:13:02 +0530 Subject: [PATCH 13/31] Accessibility label changes for heart. --- MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 7 ++----- MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift | 2 +- .../SupportingFiles/Strings/en.lproj/Localizable.strings | 4 ++-- .../Strings/es-MX.lproj/Localizable.strings | 3 ++- .../SupportingFiles/Strings/es.lproj/Localizable.strings | 3 ++- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index eeda08af..796bf617 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -79,7 +79,6 @@ import UIKit heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1) heightConstraint?.isActive = true isAccessibilityElement = true - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "heart_action_hint") accessibilityTraits = .button updateAccessibilityLabel() } @@ -97,10 +96,8 @@ import UIKit //-------------------------------------------------- /// Adjust accessibility label based on selection of Heart. func updateAccessibilityLabel() { - if let message = MVMCoreUIUtility.hardcodedString(withKey: "heart"), - let selectedState = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_selected_state" : "heart_not_selected_state") { - accessibilityLabel = message + selectedState - } + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_unfavorite_action_hint" : "heart_favorite_action_hint") + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_selected_state" : "heart_not_selected_state") } func tapAction() { diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift index b3d168ef..8001ddd5 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift @@ -60,6 +60,6 @@ open class HeartModel: MoleculeModelProtocol { try container.encode(isActive, forKey: .isActive) try container.encode(activeColor, forKey: .activeColor) try container.encode(inActiveColor, forKey: .inActiveColor) - try container.encodeModelIfPresent(action, forKey: .action) + try container.encodeModel(action, forKey: .action) } } diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index db90b9b7..bbeadbdb 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -68,8 +68,8 @@ "AccToggleHint" = "double tap to toggle"; // MARK: Heart -"heart" = "Favorite"; -"heart_action_hint" = "Double tap to favorite"; +"heart_favorite_action_hint" = "Double tap to favorite"; +"heart_unfavorite_action_hint" = "Double tap to unfavorite"; "heart_selected_state" = "Favorited"; "heart_not_selected_state" = "Unfavorited"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings index d6bfb89c..0a37e1a5 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings @@ -50,7 +50,8 @@ "AccOff" = "apagado"; "AccToggleHint" = "toca dos veces para alternar"; // Heart -"heart_action_hint" = "Toca dos veces en favorito"; +"heart_favorite_action_hint" = "Toca dos veces en favorito"; +"heart_unfavorite_action_hint" = "Toca dos veces para dejar de marcar como favorito"; "heart_selected_state" = "Favoritos"; "heart_not_selected_state" = "No favorito"; // Carousel diff --git a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings index b06c972b..a64c3915 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings @@ -53,7 +53,8 @@ "AccToggleHint" = "toca dos veces para alternar"; // Heart -"heart_action_hint" = "Toca dos veces en favorito"; +"heart_favorite_action_hint" = "Toca dos veces en favorito"; +"heart_unfavorite_action_hint" = "Toca dos veces para dejar de marcar como favorito"; "heart_selected_state" = "Favoritos"; "heart_not_selected_state" = "No favorito"; From fc9272d0b782056d250c4d198a81179ac0257bfe Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 7 Oct 2020 15:36:02 -0400 Subject: [PATCH 14/31] MFViewController Model Dynamic Root Non Root navigation item --- MVMCoreUI.xcodeproj/project.pbxproj | 4 +++ MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 4 +-- .../NavigationBar/NavigationItemModel.swift | 28 +++++++-------- MVMCoreUI/Atomic/Protocols/PageProtocol.swift | 28 +++++++++++++++ .../Atomic/Protocols/TemplateProtocol.swift | 2 +- .../BaseControllers/ViewController.swift | 35 +++++++++++-------- .../Containers/NavigationController.swift | 7 ++-- .../MVMCoreUIDetailViewProtocol.h | 6 ++++ ...MCoreUISplitViewController+Extension.swift | 3 +- 9 files changed, 81 insertions(+), 36 deletions(-) create mode 100644 MVMCoreUI/Atomic/Protocols/PageProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 72326c6c..0b0e49c6 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -303,6 +303,7 @@ D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; }; D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; }; + D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B43252E00E4004B3F56 /* PageProtocol.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; @@ -796,6 +797,7 @@ D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = ""; }; D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = ""; }; + D20F3B43252E00E4004B3F56 /* PageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageProtocol.swift; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.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 = ""; }; @@ -2077,6 +2079,7 @@ children = ( 012A88C7238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, + D20F3B43252E00E4004B3F56 /* PageProtocol.swift */, 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */, 011B58EE23A2AA850085F53C /* ModelProtocols */, @@ -2345,6 +2348,7 @@ 525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */, D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */, D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */, + D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */, 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */, 94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */, D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 9ebb8cfb..2e228ee5 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -35,12 +35,12 @@ import Foundation } /// Convenience function for legacy classes - public func getMoleculeModelForJSON(_ json: [String: Any]) throws -> MoleculeModelProtocol? { + public func getMoleculeModelForJSON(_ json: [String: Any], delegateObject: DelegateObject? = nil) throws -> MoleculeModelProtocol? { guard let moleculeName = json.optionalStringForKey(KeyMoleculeName), let type = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else { throw ModelRegistry.Error.decoderErrorModelNotMapped() } - guard let model = try type.decode(jsonDict: json) as? MoleculeModelProtocol else { + guard let model = try type.decode(jsonDict: json, delegateObject: delegateObject) as? MoleculeModelProtocol else { throw ModelRegistry.Error.decoderError } return model diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index d8c8c7ac..15ef632e 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -8,25 +8,25 @@ import Foundation -public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { - public class var identifier: String { +open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { + open class var identifier: String { return "navigationBar" } - public var title: String? - public var hidden: Bool - public var backgroundColor: Color? - public var tintColor: Color - public var line: LineModel? - public var hidesSystemBackButton = true + open var title: String? + open var hidden: Bool + open var backgroundColor: Color? + open var tintColor: Color + open var line: LineModel? + open var hidesSystemBackButton = true /// If true, we add the button in the backButton property. If false we do not add the button. If nil, we add the button if the controller is not the bottom of the stack - public var alwaysShowBackButton: Bool? - public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? + open var alwaysShowBackButton: Bool? + open var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? - public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? - public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? - public var titleView: MoleculeModelProtocol? + open var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? + open var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? + open var titleView: MoleculeModelProtocol? public init() { hidden = false @@ -44,8 +44,6 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt case line case alwaysShowBackButton case backButton - case showLeftPanelButton - case showRightPanelButton case additionalLeftButtons case additionalRightButtons case titleView diff --git a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift new file mode 100644 index 00000000..3c2a338b --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift @@ -0,0 +1,28 @@ +// +// PageProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/7/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol PageProtocol { + var pageModel: PageModelProtocol? { get set } + + mutating func updateNavigation(with model: NavigationItemModelProtocol & MoleculeModelProtocol) +} + +public extension PageProtocol where Self: UIViewController { + mutating func updateNavigation(with model: NavigationItemModelProtocol & MoleculeModelProtocol) { + pageModel?.navigationBar = model + if var manager = ((self as? MVMCoreViewManagerViewControllerProtocol)?.manager as? PageProtocol) { + // Go through the manager if possible. + manager.updateNavigation(with: model) + } else if let navigationController = navigationController { + NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: self) + MVMCoreUISplitViewController.setNavigationBarUI(for: self, navigationController: navigationController, navigationItemModel: model, leftPanelAccessible: (self as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?(), rightPanelAccessible: (self as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?()) + } + } +} diff --git a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift index 4696ce38..04bc080b 100644 --- a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift @@ -23,7 +23,7 @@ public extension TemplateProtocol where Self: ViewController { let decoder = JSONDecoder() try decoder.add(delegateObject: delegateObjectIVar) self.templateModel = try decodeTemplate(using: decoder, from: data) - self.pageModel = templateModel as? MVMControllerModelProtocol + self.model = templateModel as? MVMControllerModelProtocol } func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel { diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index f0d6ab84..dcd02c40 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -8,15 +8,23 @@ import UIKit - -@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol { +@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol, PageProtocol { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @objc public var pageType: String? @objc public var loadObject: MVMCoreLoadObject? - public var pageModel: MVMControllerModelProtocol? + public var model: MVMControllerModelProtocol? + public var pageModel: PageModelProtocol? { + get { + return model + } + set { + model = newValue as? MVMControllerModelProtocol + } + } /// Set if this page is containted in a manager. public var manager: (UIViewController & MVMCoreViewManagerProtocol)? @@ -214,18 +222,18 @@ import UIKit /// Creates a legacy navigation model. open func createDefaultLegacyNavigationModel() -> NavigationItemModel { let navigationModel = NavigationItemModel() - navigationModel.title = pageModel?.screenHeading + navigationModel.title = model?.screenHeading return navigationModel } /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, open func handleNewData() { if formValidator == nil { - let rules = pageModel?.formRules + let rules = model?.formRules formValidator = FormValidator(rules) } - if let backgroundColor = pageModel?.backgroundColor { + if let backgroundColor = model?.backgroundColor { view.backgroundColor = backgroundColor.uiColor } @@ -239,11 +247,11 @@ import UIKit open func getNavigationModel() -> NavigationItemModelProtocol? { // TODO: remove legacy. Temporary, convert legacy to navigation model. - if pageModel?.navigationBar == nil { + if model?.navigationBar == nil { let navigationItem = createDefaultLegacyNavigationModel() - pageModel?.navigationBar = navigationItem + model?.navigationBar = navigationItem } - return pageModel?.navigationBar + return model?.navigationBar } /// Sets the navigation item for this view controller. @@ -252,9 +260,6 @@ import UIKit let navigationController = navigationController else { return } - // We additionally want our left items - navigationItem.leftItemsSupplementBackButton = true - // Utilize helper function to set the navigation item state. NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) } @@ -324,7 +329,7 @@ import UIKit guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self else { return } MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar - if let index = (pageModel as? TabPageModelProtocol)?.tabBarIndex { + if let index = (model as? TabPageModelProtocol)?.tabBarIndex { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) } else if let index = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) @@ -335,7 +340,7 @@ import UIKit self.tabBarIndex = index } - if let hidden = (pageModel as? TabPageModelProtocol)?.tabBarHidden { + if let hidden = (model as? TabPageModelProtocol)?.tabBarHidden { MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) } else if let hidden = loadObject?.requestParameters?.actionMap?["tabBarHidden"] as? Bool { MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) @@ -594,6 +599,6 @@ import UIKit //-------------------------------------------------- func executeBehaviors(_ behaviorBlock:(_ behavior:T)->Void) { - pageModel?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } + model?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } } } diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 22864027..89ea446c 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -48,6 +48,7 @@ import UIKit viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title viewController.navigationItem.hidesBackButton = navigationItemModel.hidesSystemBackButton + viewController.navigationItem.leftItemsSupplementBackButton = !navigationItemModel.hidesSystemBackButton setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) } @@ -97,7 +98,8 @@ import UIKit /// Convenience setter for legacy files public static func setNavigationItem(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") } setNavigationItem(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) @@ -105,7 +107,8 @@ import UIKit /// Convenience setter for legacy files public static func setNavigationBarUI(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") } setNavigationBarUI(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h index 2ec18da9..60ce0873 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h @@ -15,6 +15,12 @@ NS_ASSUME_NONNULL_BEGIN @optional +/// Returns if the left panel should be accessible. +- (BOOL)isLeftPanelAccessible; + +/// Returns if the right panel should be accessible. +- (BOOL)isRightPanelAccessible; + - (void)panelWillAppear:(nonnull NSObject *)panel; - (void)panelWillAppear:(nonnull NSObject *)panel overtakingDetail:(BOOL)willOvertake; - (void)panelDidAppear:(nonnull NSObject *)panel; diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index d37d88f1..8e14720d 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -111,7 +111,8 @@ public extension MVMCoreUISplitViewController { // MARK: - Legacy Functions /// Convenience setter for legacy files. Sets the navigation item for the view controller based on the json and splitview controller @objc static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationJSON: [String: Any], leftPanelAccessible: Bool, rightPanelAccessible: Bool, progress: NSNumber?) throws { - guard let navigationItemModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + guard let navigationItemModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") } guard let splitView = MVMCoreUISplitViewController.main(), From e60824ee1b4f38606a1f498396efa3c0d339575a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 7 Oct 2020 18:15:52 -0400 Subject: [PATCH 15/31] Navigation Refresh protocol --- MVMCoreUI/Atomic/Protocols/PageProtocol.swift | 37 +++++++++++++++---- .../BaseControllers/ViewController.swift | 7 +++- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift index 3c2a338b..c96742a4 100644 --- a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift @@ -11,18 +11,39 @@ import Foundation public protocol PageProtocol { var pageModel: PageModelProtocol? { get set } - mutating func updateNavigation(with model: NavigationItemModelProtocol & MoleculeModelProtocol) + /// Sets the navigationBar property of the pageModel and attempts to refresh the ui. + mutating func updateNavigationBar(with model: NavigationItemModelProtocol & MoleculeModelProtocol) } -public extension PageProtocol where Self: UIViewController { - mutating func updateNavigation(with model: NavigationItemModelProtocol & MoleculeModelProtocol) { +extension PageProtocol { + mutating public func updateNavigationBar(with model: NavigationItemModelProtocol & MoleculeModelProtocol) { pageModel?.navigationBar = model - if var manager = ((self as? MVMCoreViewManagerViewControllerProtocol)?.manager as? PageProtocol) { + (self as? NavigationBarRefreshProtocol)?.refreshNavigationUI() + } +} + +/// A protocol to inform that we should refresh the navigation bar ui. +@objc public protocol NavigationBarRefreshProtocol { + @objc func refreshNavigationUI() +} + +public extension UINavigationBar { + + /// Convenience function to refresh the navigation bar ui. + @objc static func refreshNavigationUI(for viewController: UIViewController) { + guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } + if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { // Go through the manager if possible. - manager.updateNavigation(with: model) - } else if let navigationController = navigationController { - NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: self) - MVMCoreUISplitViewController.setNavigationBarUI(for: self, navigationController: navigationController, navigationItemModel: model, leftPanelAccessible: (self as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?(), rightPanelAccessible: (self as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?()) + manager.refreshNavigationUI() + } else if let navigationController = viewController.navigationController { + NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) + MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model, leftPanelAccessible: (viewController as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?(), rightPanelAccessible: (viewController as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?()) } } } + +extension UIViewController: NavigationBarRefreshProtocol { + public func refreshNavigationUI() { + UINavigationBar.refreshNavigationUI(for: self) + } +} diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index dcd02c40..a751f2b1 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -121,7 +121,12 @@ import UIKit // Update navigation bar if showing. if MVMCoreUIUtility.getCurrentVisibleController() == self { - self.setNavigationBar() + if let manager = self.manager { + // Let manager handle + manager.refreshNavigationUI() + } else { + self.setNavigationBar() + } } }) } catch { From 43bd436225293604528326a4cb1799627dd37e32 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 8 Oct 2020 11:31:40 -0400 Subject: [PATCH 16/31] Added view controller getter. --- MVMCoreUI/Containers/NavigationController.swift | 2 +- .../MVMCoreUISplitViewController+Extension.swift | 2 +- .../SplitViewController/MVMCoreUISplitViewController.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 89ea446c..681642f8 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -59,7 +59,7 @@ import UIKit var leftItems: [UIBarButtonItem] = [] if navigationItemModel.alwaysShowBackButton != false { if let backButtonModel = navigationItemModel.backButton, - navigationController.viewControllers.count > 1 || navigationItemModel.alwaysShowBackButton ?? false { + MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 0 > 1 || navigationItemModel.alwaysShowBackButton ?? false { leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) } if let leftItemModels = navigationItemModel.additionalLeftButtons { diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index 8e14720d..2f452647 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -48,7 +48,7 @@ public extension MVMCoreUISplitViewController { if let forceBackButton = navigationItemModel?.alwaysShowBackButton { showBackButton = forceBackButton } else { - showBackButton = navigationController.viewControllers.count > 1 + showBackButton = MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 0 > 1 } if showBackButton { if let backButtonModel = navigationItemModel?.backButton { diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 177d0b8a..d88641fa 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -241,7 +241,7 @@ CGFloat const PanelAnimationDuration = 0.2; - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *leftBarButtonItems = [NSMutableArray array]; - if ([viewController.navigationController.viewControllers count] > 1) { + if (viewController.navigationController && [MVMCoreNavigationHandler.sharedNavigationHandler getViewControllersForNavigationController:viewController.navigationController].count > 1) { [leftBarButtonItems addObject:self.backButton]; } if ((accessible && !extended) && self.leftPanelButton) { From 101e1422f3595716d0c560dcd6677ef3409d8185 Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Thu, 8 Oct 2020 21:01:55 +0530 Subject: [PATCH 17/31] scroll view indicator fix for defect: CXTDT-82115 --- MVMCoreUI/BaseControllers/ScrollingViewController.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift index eb1d5999..7b368e2a 100644 --- a/MVMCoreUI/BaseControllers/ScrollingViewController.swift +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -50,6 +50,14 @@ open class ScrollingViewController: ViewController { dismissKeyboardTapGesture?.isEnabled = false scrollView.alwaysBounceVertical = false scrollView.delegate = self + // will change scrollView indicatorStyle automatically on the basis of backgroundColor + var componentWhite:CGFloat = 0 + var componentAlpha:CGFloat = 0 + if let grayScale = view.backgroundColor?.getWhite(&componentWhite, alpha: &componentAlpha), grayScale + { + scrollView.indicatorStyle = componentWhite > 0 ? .black : .white + } + scrollView.flashScrollIndicators() } open override func viewDidLayoutSubviews() { From 2e12464016e406f8be5ff3d18731dfe36f80bfeb Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 8 Oct 2020 15:01:38 -0400 Subject: [PATCH 18/31] decouple manager navigation setting a little better --- MVMCoreUI/Atomic/Protocols/PageProtocol.swift | 21 +-- .../BaseControllers/ViewController.swift | 122 +++++++++--------- ...MCoreUISplitViewController+Extension.swift | 38 ++---- 3 files changed, 77 insertions(+), 104 deletions(-) diff --git a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift index c96742a4..8cd8bf34 100644 --- a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift @@ -10,16 +10,6 @@ import Foundation public protocol PageProtocol { var pageModel: PageModelProtocol? { get set } - - /// Sets the navigationBar property of the pageModel and attempts to refresh the ui. - mutating func updateNavigationBar(with model: NavigationItemModelProtocol & MoleculeModelProtocol) -} - -extension PageProtocol { - mutating public func updateNavigationBar(with model: NavigationItemModelProtocol & MoleculeModelProtocol) { - pageModel?.navigationBar = model - (self as? NavigationBarRefreshProtocol)?.refreshNavigationUI() - } } /// A protocol to inform that we should refresh the navigation bar ui. @@ -32,12 +22,13 @@ public extension UINavigationBar { /// Convenience function to refresh the navigation bar ui. @objc static func refreshNavigationUI(for viewController: UIViewController) { guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } - if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { - // Go through the manager if possible. - manager.refreshNavigationUI() - } else if let navigationController = viewController.navigationController { + if let navigationController = viewController.navigationController { NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) - MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model, leftPanelAccessible: (viewController as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?(), rightPanelAccessible: (viewController as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?()) + MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model) + } + if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { + // Refresh the manager if possible. + manager.refreshNavigationUI() } } } diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index a751f2b1..e7563faa 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -119,14 +119,15 @@ import UIKit MVMCoreDispatchUtility.performBlock(onMainThread: { self.handleNewDataAndUpdateUI() - // Update navigation bar if showing. if MVMCoreUIUtility.getCurrentVisibleController() == self { - if let manager = self.manager { - // Let manager handle - manager.refreshNavigationUI() - } else { - self.setNavigationBar() - } + // Update navigation bar if showing. + self.setNavigationBar() + self.manager?.refreshNavigationUI() + } + // Update splitview properties + if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { + MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(self.bottomProgress() ?? 0) + self.updateTabBar() } }) } catch { @@ -140,7 +141,7 @@ import UIKit pageType = loadObject.pageType self.loadObject = loadObject - // Verifies all modules needed are loaded. TODO: change to ViewController + // Verifies all modules needed are loaded. guard ViewController.verifyRequiredModulesLoaded(for: loadObject, error: error) else { return false } // Parse the model for the page. @@ -247,7 +248,7 @@ import UIKit } //-------------------------------------------------- - // MARK: - Navigation Item (Move to model base) + // MARK: - Navigation Item //-------------------------------------------------- open func getNavigationModel() -> NavigationItemModelProtocol? { @@ -271,61 +272,16 @@ import UIKit /// Sets the appearance of the navigation bar based on the model. open func setNavigationBar() { - let viewController = manager ?? self guard let navigationItemModel = getNavigationModel(), - let navigationController = viewController.navigationController else { + let navigationController = navigationController else { MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() return } // Utilize helper function to set the split view and navigation item state. - MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: isMasterInitiallyAccessible(), rightPanelAccessible: isSupportInitiallyAccessible(), progress: bottomProgress() ?? 0) + MVMCoreUISplitViewController.setNavigationBarUI(for: self, navigationController: navigationController, navigationItemModel: navigationItemModel) } - // Eventually will be moved to server - open func isMasterInitiallyAccessible() -> Bool { - if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { - return false - } - return MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false - } - - // Eventually will be moved to server - open func isSupportInitiallyAccessible() -> Bool { - if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { - return false - } - return (MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false) || showRightPanelForScreenBeforeLaunchApp() - } - - open func showRightPanelForScreenBeforeLaunchApp() -> Bool { - return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false - } - - // Eventually will be moved to separate button in navigation item model - open func isOverridingRightButton() -> Bool { - guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") - else { return false } - MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) - return true - } - - // Eventually will be moved to separate button in navigation item model - open func isOverridingLeftButton() -> Bool { - guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") - else { return false } - MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) - return true - } - - // Eventually will be moved to Model - open func bottomProgress() -> Float? { - guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), - let progress = Float(progressString) - else { return nil } - - return progress / Float(100) - } //-------------------------------------------------- // MARK: - TabBar //-------------------------------------------------- @@ -408,14 +364,15 @@ import UIKit } open func pageShown() { - // Update the navigation bar ui when view is appearing. + // Update split view properties if this is the current detail controller. if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { MVMCoreUISplitViewController.main()?.setupPanels() + MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(bottomProgress() ?? 0) + updateTabBar() } - setNavigationBar() - // Update tab if needed. - updateTabBar() + // Update the navigation bar ui when view is appearing. + setNavigationBar() // Track. MVMCoreUISession.sharedGlobal()?.currentPageType = pageType @@ -536,6 +493,51 @@ import UIKit setNavigationBar() } + public func isLeftPanelAccessible() -> Bool { + // TODO: Remove when hamburger menu is fully phased out. + if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { + return false + } + return MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false + } + + public func isRightPanelAccessible() -> Bool { + // TODO: Remove when FAB is 100%. + if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { + return false + } + return (MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false) || showRightPanelForScreenBeforeLaunchApp() + } + + open func showRightPanelForScreenBeforeLaunchApp() -> Bool { + return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false + } + + // TODO: make molecular + open func isOverridingRightButton() -> Bool { + guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") + else { return false } + MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) + return true + } + + // TODO: make molecular + open func isOverridingLeftButton() -> Bool { + guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") + else { return false } + MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) + return true + } + + // Eventually will be moved to Model + open func bottomProgress() -> Float? { + guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), + let progress = Float(progressString) + else { return nil } + + return progress / Float(100) + } + //-------------------------------------------------- // MARK: - UITextFieldDelegate //-------------------------------------------------- diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index 2f452647..eb22eccb 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -10,32 +10,29 @@ import Foundation public extension MVMCoreUISplitViewController { - /// Convenience function. Sets the navigation and split view properties for the view controller. Optional parameters use current value if not set. - static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, leftPanelAccessible: Bool? = nil, rightPanelAccessible: Bool? = nil, progress: Float? = nil) { + /// Convenience function. Sets the navigation and split view properties for the view controller. Panel access is determined if view controller is a detail view protocol. + static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { guard let splitView = MVMCoreUISplitViewController.main(), navigationController == splitView.navigationController, navigationController.topViewController == viewController else { - NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - return + /// Not the split view navigation controller, skip split functions. + NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + return } - splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: leftPanelAccessible, rightPanelAccessible: rightPanelAccessible, progress: progress) + splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel) } /// Sets the navigation item for the view controller based on the model and splitview controller - private func set(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, leftPanelAccessible: Bool? = nil, rightPanelAccessible: Bool? = nil, progress: Float? = nil) { + private func set(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - setLeftPanelIsAccessible(leftPanelAccessible ?? leftPanelIsAccessible, for: viewController, updateNavigationButtons: false) - setRightPanelIsAccessible(rightPanelAccessible ?? rightPanelIsAccessible, for: viewController, updateNavigationButtons: false) + setLeftPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) + setRightPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) setLeftNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setRightNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationIconColor(navigationItemModel.tintColor.uiColor) - - if let progress = progress { - setBottomProgressBarProgress(progress) - } } /// Sets the left navigation items for the view controller based on model and splitview. @@ -107,21 +104,4 @@ public extension MVMCoreUISplitViewController { viewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue) } - - // MARK: - Legacy Functions - /// Convenience setter for legacy files. Sets the navigation item for the view controller based on the json and splitview controller - @objc static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationJSON: [String: Any], leftPanelAccessible: Bool, rightPanelAccessible: Bool, progress: NSNumber?) throws { - let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject - guard let navigationItemModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { - throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") - } - guard let splitView = MVMCoreUISplitViewController.main(), - navigationController == splitView.navigationController, - navigationController.topViewController == viewController else { - NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - return - } - let progress = progress?.floatValue - splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: leftPanelAccessible, rightPanelAccessible: rightPanelAccessible, progress: progress) - } } From 20369d5a1e959b1d0f4018bcc6705ebeee9cf10f Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 8 Oct 2020 15:10:23 -0400 Subject: [PATCH 19/31] Minor cleanup --- MVMCoreUI.xcodeproj/project.pbxproj | 4 +++ .../NavigationBarRefreshProtocol.swift | 34 +++++++++++++++++++ MVMCoreUI/Atomic/Protocols/PageProtocol.swift | 27 --------------- .../Containers/NavigationController.swift | 20 +---------- 4 files changed, 39 insertions(+), 46 deletions(-) create mode 100644 MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 0b0e49c6..1755d203 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -304,6 +304,7 @@ D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; }; D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; }; D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B43252E00E4004B3F56 /* PageProtocol.swift */; }; + D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; @@ -798,6 +799,7 @@ D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = ""; }; D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = ""; }; D20F3B43252E00E4004B3F56 /* PageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageProtocol.swift; sourceTree = ""; }; + D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarRefreshProtocol.swift; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.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 = ""; }; @@ -2082,6 +2084,7 @@ D20F3B43252E00E4004B3F56 /* PageProtocol.swift */, 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */, + D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */, 011B58EE23A2AA850085F53C /* ModelProtocols */, ); path = Protocols; @@ -2431,6 +2434,7 @@ D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */, + D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */, D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */, D260105D23D0BCD400764D80 /* Stack.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift new file mode 100644 index 00000000..589aa4ab --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift @@ -0,0 +1,34 @@ +// +// NavigationBarRefreshProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// A protocol to inform that we should refresh the navigation bar ui. +@objc public protocol NavigationBarRefreshProtocol { + @objc func refreshNavigationUI() +} + +extension UIViewController: NavigationBarRefreshProtocol { + + /// Convenience function to refresh the navigation bar ui. A separate function for others to use. + @objc static func refreshNavigationUI(for viewController: UIViewController) { + guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } + if let navigationController = viewController.navigationController { + NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) + MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model) + } + if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { + // Refresh the manager if possible. + manager.refreshNavigationUI() + } + } + + public func refreshNavigationUI() { + UIViewController.refreshNavigationUI(for: self) + } +} diff --git a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift index 8cd8bf34..4117604e 100644 --- a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift @@ -11,30 +11,3 @@ import Foundation public protocol PageProtocol { var pageModel: PageModelProtocol? { get set } } - -/// A protocol to inform that we should refresh the navigation bar ui. -@objc public protocol NavigationBarRefreshProtocol { - @objc func refreshNavigationUI() -} - -public extension UINavigationBar { - - /// Convenience function to refresh the navigation bar ui. - @objc static func refreshNavigationUI(for viewController: UIViewController) { - guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } - if let navigationController = viewController.navigationController { - NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) - MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model) - } - if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { - // Refresh the manager if possible. - manager.refreshNavigationUI() - } - } -} - -extension UIViewController: NavigationBarRefreshProtocol { - public func refreshNavigationUI() { - UINavigationBar.refreshNavigationUI(for: self) - } -} diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 681642f8..7a403338 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -95,25 +95,7 @@ import UIKit navigationController.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none } } - - /// Convenience setter for legacy files - public static func setNavigationItem(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { - throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") - } - setNavigationItem(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) - } - - /// Convenience setter for legacy files - public static func setNavigationBarUI(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { - throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") - } - setNavigationBarUI(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) - } - + /// Convenience function for setting the navigation titleView. public static func setNavigationTitleView(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject From b89e414cb79b3401f0129dc88b9130cf79a5e952 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 8 Oct 2020 15:27:47 -0400 Subject: [PATCH 20/31] missed commits --- MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift index 589aa4ab..2d4002e3 100644 --- a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift @@ -16,7 +16,7 @@ import Foundation extension UIViewController: NavigationBarRefreshProtocol { /// Convenience function to refresh the navigation bar ui. A separate function for others to use. - @objc static func refreshNavigationUI(for viewController: UIViewController) { + @objc public static func refreshNavigationUI(for viewController: UIViewController) { guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } if let navigationController = viewController.navigationController { NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) From 6c1a8ba330bb8f52b7d07cd39f22d2232eb8b917 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 8 Oct 2020 19:52:46 -0400 Subject: [PATCH 21/31] modal section list --- MVMCoreUI.xcodeproj/project.pbxproj | 20 ++++++++---- .../Templates/ModalSectionListTemplate.swift | 30 +++++++++++++++++ .../ModalSectionListTemplateModel.swift | 32 +++++++++++++++++++ ...iewControllerMappingObject+Extension.swift | 1 + 4 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift create mode 100644 MVMCoreUI/Atomic/Templates/ModalSectionListTemplateModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 45211c90..f9ec5dc4 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -469,6 +469,8 @@ D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */; }; D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */; }; D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */; }; + D2D3957A252FDBB300047B11 /* ModalSectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */; }; + D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D3957C252FDBCD00047B11 /* ModalSectionListTemplateModel.swift */; }; D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; }; D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; }; D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; }; @@ -486,12 +488,12 @@ D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; }; D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; }; D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; }; - D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; }; - D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; }; - D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */; }; D2EC7BD52527B7A600F540AF /* MoleculeSectionHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */; }; D2EC7BD92527B7CF00F540AF /* MoleculeSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */; }; D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */; }; + D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; }; + D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; }; + D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; }; D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */; }; @@ -971,6 +973,8 @@ D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertExpandableView+Extension.swift"; sourceTree = ""; }; D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooterModel.swift; sourceTree = ""; }; D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooter.swift; sourceTree = ""; }; + D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalSectionListTemplate.swift; sourceTree = ""; }; + D2D3957C252FDBCD00047B11 /* ModalSectionListTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalSectionListTemplateModel.swift; sourceTree = ""; }; D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = ""; }; D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = ""; }; D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = ""; }; @@ -987,12 +991,12 @@ D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = ""; }; D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = ""; }; D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = ""; }; - D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = ""; }; - D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = ""; }; - D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationTopView.swift; sourceTree = ""; }; D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeaderModel.swift; sourceTree = ""; }; D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeader.swift; sourceTree = ""; }; D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderFooterView.swift; sourceTree = ""; }; + D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = ""; }; + D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = ""; }; + D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationTopView.swift; sourceTree = ""; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = ""; }; D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = ""; }; D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityProtocol.swift; sourceTree = ""; }; @@ -1707,6 +1711,8 @@ D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */, D2169302251E53D9002A6324 /* SectionListTemplateModel.swift */, D2169300251E51E7002A6324 /* SectionListTemplate.swift */, + D2D3957C252FDBCD00047B11 /* ModalSectionListTemplateModel.swift */, + D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */, ); path = Templates; sourceTree = ""; @@ -2559,6 +2565,7 @@ 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */, AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */, + D2D3957A252FDBB300047B11 /* ModalSectionListTemplate.swift in Sources */, DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */, D28BA741248025A300B75CB8 /* TabBarModel.swift in Sources */, D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */, @@ -2671,6 +2678,7 @@ D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */, BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */, C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, + D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */, 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */, D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift new file mode 100644 index 00000000..77ee705c --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift @@ -0,0 +1,30 @@ +// +// ModalSectionListTemplate.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class ModalSectionListTemplate: SectionListTemplate { + + // For subclassing the model. + open override func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> ModalSectionListTemplateModel { + return try decoder.decode(ModalSectionListTemplateModel.self, from: data) + } + + override open func handleNewData() { + super.handleNewData() + _ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in + guard let self = self else { return } + guard let model = self.templateModel as? ModalSectionListTemplateModel, + let actionMap = model.closeAction else { + MVMCoreActionHandler.shared()?.handleAction(with: ActionBackModel().toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar) + return + } + MVMCoreActionHandler.shared()?.handleAction(with: actionMap.toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar) + }) + } +} diff --git a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplateModel.swift new file mode 100644 index 00000000..83f2bb98 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplateModel.swift @@ -0,0 +1,32 @@ +// +// ModalSectionListTemplateModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class ModalSectionListTemplateModel: SectionListTemplateModel { + public override class var identifier: String { + return "modalSectionList" + } + public var closeAction: ActionModelProtocol? + + private enum CodingKeys: String, CodingKey { + case closeAction + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + closeAction = try typeContainer.decodeModelIfPresent(codingKey: .closeAction) + 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.encodeModelIfPresent(closeAction, forKey: .closeAction) + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift index efd70570..f126799c 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift @@ -21,6 +21,7 @@ public extension MVMCoreUIViewControllerMappingObject { register(template: MoleculeListTemplate.self) add(toTemplateViewControllerMapping: ["modalList": MVMCoreViewControllerProgrammaticMappingObject(with: ModalMoleculeListTemplate.self)!]) add(toTemplateViewControllerMapping: [SectionListTemplateModel.identifier: MVMCoreViewControllerProgrammaticMappingObject(with: SectionListTemplate.self)!]) + add(toTemplateViewControllerMapping: [ModalSectionListTemplateModel.identifier: MVMCoreViewControllerProgrammaticMappingObject(with: ModalSectionListTemplate.self)!]) register(template: ThreeLayerTemplate.self) register(template: ThreeLayerCenterTemplate.self) From 24864bc99db1ea7da95a8441067661c802f7be4d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 9 Oct 2020 10:51:52 -0400 Subject: [PATCH 22/31] Polling top notification --- .../Atomic/Molecules/OtherContainers/MoleculeContainer.swift | 4 ++-- .../Molecules/OtherContainers/MoleculeContainerModel.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift index 4e2c6279..d6b795ce 100644 --- a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift @@ -11,11 +11,11 @@ import UIKit open class MoleculeContainer: Container { /// Can be overriden to change how the molecule is added to the hierarchy. - public func addMolecule(_ molecule: UIView) { + open func addMolecule(_ molecule: UIView) { addAndContain(molecule) } - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { if let casteModel = model as? MoleculeContainerModelProtocol { if view != nil { (view as? MoleculeViewProtocol)?.set(with: casteModel.molecule, delegateObject, additionalData) diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift index aa847f88..77b40716 100644 --- a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift @@ -9,7 +9,7 @@ import Foundation open class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtocol, MoleculeModelProtocol { - public class var identifier: String { + open class var identifier: String { return "container" } public var backgroundColor: Color? @@ -41,7 +41,7 @@ open class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtoco try super.init(from: decoder) } - public override func encode(to encoder: Encoder) throws { + open override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) From 16f8989036d5898cd504e480ac33a01ccc953a0a Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Fri, 9 Oct 2020 23:03:02 +0530 Subject: [PATCH 23/31] implemented review feedback --- .../ScrollingViewController.swift | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift index 7b368e2a..fcbcf878 100644 --- a/MVMCoreUI/BaseControllers/ScrollingViewController.swift +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -50,14 +50,6 @@ open class ScrollingViewController: ViewController { dismissKeyboardTapGesture?.isEnabled = false scrollView.alwaysBounceVertical = false scrollView.delegate = self - // will change scrollView indicatorStyle automatically on the basis of backgroundColor - var componentWhite:CGFloat = 0 - var componentAlpha:CGFloat = 0 - if let grayScale = view.backgroundColor?.getWhite(&componentWhite, alpha: &componentAlpha), grayScale - { - scrollView.indicatorStyle = componentWhite > 0 ? .black : .white - } - scrollView.flashScrollIndicators() } open override func viewDidLayoutSubviews() { @@ -71,6 +63,16 @@ open class ScrollingViewController: ViewController { registerForKeyboardNotifications() } + open override func handleNewData() { + super.handleNewData() + // will change scrollView indicatorStyle automatically on the basis of backgroundColor + var greyScale: CGFloat = 0 + if view.backgroundColor?.getWhite(&greyScale, alpha: nil) ?? false { + scrollView.indicatorStyle = greyScale > 0.5 ? .black : .white + } + scrollView.flashScrollIndicators() + } + //-------------------------------------------------- // MARK: - Keyboard Handling //-------------------------------------------------- From 35468f592e4956275642e2aef2c6bd6d638a7f9d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 9 Oct 2020 14:19:56 -0400 Subject: [PATCH 24/31] accessibility adjustment --- MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index bbeadbdb..ee0b1849 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -71,7 +71,7 @@ "heart_favorite_action_hint" = "Double tap to favorite"; "heart_unfavorite_action_hint" = "Double tap to unfavorite"; "heart_selected_state" = "Favorited"; -"heart_not_selected_state" = "Unfavorited"; +"heart_not_selected_state" = "Un-favorited"; // MARK: Carousel "MVMCoreUIPageControl_currentpage_index" = "page %@ of %d"; From a51a0e458bee5598a7cea73f10b9f26d3fc0d056 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Sun, 11 Oct 2020 16:04:28 -0400 Subject: [PATCH 25/31] fix --- MVMCoreUI/BaseControllers/ViewController.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index e7563faa..528c63cb 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -439,13 +439,17 @@ import UIKit MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true) } } - + + // Override this method to avoid adding form params. + open func addFormParams(_ requestParameters: MVMCoreRequestParameters) { + formValidator?.addFormParams(requestParameters: requestParameters) + } //-------------------------------------------------- // MARK: - MVMCoreActionDelegateProtocol //-------------------------------------------------- open func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { - formValidator?.addFormParams(requestParameters: requestParameters) + addFormParams(requestParameters) requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") MVMCoreActionHandler.defaultHandleOpenPage(for: requestParameters, additionalData: additionalData, delegateObject: delegateObject()) } From de20e77829fe7d737966750fc75911497359a5c4 Mon Sep 17 00:00:00 2001 From: "Chintakrinda, Arun Kumar (Arun)" Date: Mon, 12 Oct 2020 22:49:59 +0530 Subject: [PATCH 26/31] MoleculeName fix for MoleculeStackItem Bugfix: Headline body spilling @ traling --- MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift | 2 ++ .../Molecules/VerticalCombinationViews/HeadlineBodyLink.swift | 1 + 2 files changed, 3 insertions(+) diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift index 1f6e4d88..39fef8e2 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift @@ -20,6 +20,7 @@ import Foundation case spacing case percent case gone + case moleculeName } public override init(with moleculeModel: MoleculeModelProtocol) { @@ -39,6 +40,7 @@ import Foundation public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(spacing, forKey: .spacing) try container.encodeIfPresent(percent, forKey: .percent) try container.encode(gone, forKey: .gone) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift index da5014a9..a4228ae9 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift @@ -34,6 +34,7 @@ import UIKit headlineBody.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true headlineBody.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + headlineBody.rightAnchor.constraint(lessThanOrEqualTo: rightAnchor).isActive = true var constraint = rightAnchor.constraint(equalTo: headlineBody.rightAnchor) constraint.priority = .defaultHigh constraint.isActive = true From bdc5084ad5325cf432fb2cb7c567b416625193fd Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 12 Oct 2020 17:44:59 -0400 Subject: [PATCH 27/31] comment --- MVMCoreUI/BaseControllers/ViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 528c63cb..ebcf9f6b 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -440,7 +440,7 @@ import UIKit } } - // Override this method to avoid adding form params. + /// Override this method to avoid adding form params. open func addFormParams(_ requestParameters: MVMCoreRequestParameters) { formValidator?.addFormParams(requestParameters: requestParameters) } From c30c442562d0546c4f11b6f3b343139e9bd24b46 Mon Sep 17 00:00:00 2001 From: "Chintakrinda, Arun Kumar (Arun)" Date: Tue, 13 Oct 2020 13:08:53 +0530 Subject: [PATCH 28/31] Changed as per MR comments --- .../Molecules/VerticalCombinationViews/HeadlineBodyLink.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift index a4228ae9..1683d8e9 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift @@ -34,7 +34,7 @@ import UIKit headlineBody.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true headlineBody.leftAnchor.constraint(equalTo: leftAnchor).isActive = true - headlineBody.rightAnchor.constraint(lessThanOrEqualTo: rightAnchor).isActive = true + rightAnchor.constraint(greaterThanOrEqualTo: headlineBody.rightAnchor).isActive = true var constraint = rightAnchor.constraint(equalTo: headlineBody.rightAnchor) constraint.priority = .defaultHigh constraint.isActive = true From 159798f0d8cbc7134e64b4cb28029e5d0eb88831 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 15 Oct 2020 10:37:36 -0400 Subject: [PATCH 29/31] updates for polling --- .../TopNotification/CollapsableNotificationModel.swift | 8 ++++---- .../Molecules/TopNotification/NotificationModel.swift | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift index 804bfeb6..17b1aef7 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift @@ -8,7 +8,7 @@ import Foundation -public class CollapsableNotificationModel: NotificationModel { +open class CollapsableNotificationModel: NotificationModel { public class override var identifier: String { return "collapsableNotification" } @@ -19,12 +19,12 @@ public class CollapsableNotificationModel: NotificationModel { public var initiallyCollapsed = false public var pages: [String]? - init(with topLabel: LabelModel, headline: LabelModel) { + public init(with topLabel: LabelModel, headline: LabelModel) { self.topLabel = topLabel super.init(with: headline) } - override func setDefault() { + open override func setDefault() { super.setDefault() if topLabel.textColor == nil { topLabel.textColor = Color(uiColor: .white) @@ -61,7 +61,7 @@ public class CollapsableNotificationModel: NotificationModel { try super.init(from: decoder) } - public override func encode(to encoder: Encoder) throws { + open override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift index e2f94326..70ff00bb 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift @@ -8,7 +8,7 @@ import Foundation -public class NotificationModel: MoleculeModelProtocol { +open class NotificationModel: MoleculeModelProtocol { public class var identifier: String { return "notification" } @@ -18,11 +18,11 @@ public class NotificationModel: MoleculeModelProtocol { public var button: ButtonModel? public var closeButton: NotificationXButtonModel? - init(with headline: LabelModel) { + public init(with headline: LabelModel) { self.headline = headline } - func setDefault() { + open func setDefault() { if backgroundColor == nil { backgroundColor = Color(uiColor: .mvmGreen()) } @@ -59,7 +59,7 @@ public class NotificationModel: MoleculeModelProtocol { setDefault() } - public func encode(to encoder: Encoder) throws { + open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) From b371cf5e8374834ff3dff72ddd6c2f4eb74974bb Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 15 Oct 2020 16:36:29 -0400 Subject: [PATCH 30/31] new timers top alert update function --- .../MVMCoreUITopAlertView+Extension.swift | 46 +++++++++++++++---- MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h | 3 ++ MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 6 ++- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 8bf4860f..5c199c14 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -21,14 +21,17 @@ public extension MVMCoreUITopAlertView { NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil) } + @objc func getDelegateObject() -> MVMCoreUIDelegateObject { + // TODO: Top alert view is current delegate. Should move to current view controller eventually? + return MVMCoreUIDelegateObject.create(withDelegateForAll: self) + } + /// Checks for new top alert json @objc func responseJSONUpdated(notification: Notification) { guard let responseJSON = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON, - let json = responseJSON.optionalDictionaryForKey("TopNotification") else { return } - - // TODO: Top alert view is current delegate. Should move to current view controller eventually? - let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) - showTopAlert(with: json, delegateObject: delegateObject) + let json = responseJSON.optionalDictionaryForKey("TopNotification"), + let model = decodeTopNotification(with: json, delegateObject: getDelegateObject()) else { return } + showTopAlert(with: model) } /// When a detail page changes, check top alerts. @@ -37,24 +40,47 @@ public extension MVMCoreUITopAlertView { MVMCoreAlertHandler.shared()?.checkPagesDependency(for: controller.pageType) } - /// Shows the top alert with the json payload. - func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) { + func decodeTopNotification(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) -> TopNotificationModel? { do { - let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject) - showTopAlert(with: model, delegateObject: delegateObject) + return try TopNotificationModel.decode(json: json, delegateObject: delegateObject) } catch { if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) } + return nil } } /// Shows the top alert with the model. - func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) { + func showTopAlert(with model: TopNotificationModel) { let object = model.createTopAlertObject() MVMCoreAlertHandler.shared()?.showTopAlert(with: object) } + @objc func updateMolecule(with topAlertObject: MVMCoreTopAlertObject) { + guard topAlertObject.type == self.topAlertObject?.type else { return } + let delegateObject = getDelegateObject() + guard let newJson = topAlertObject.json, + let newModel = decodeTopNotification(with: newJson, delegateObject: delegateObject), + let newModelName = MoleculeObjectMapping.shared()?.getMoleculeClass(newModel.molecule)?.nameForReuse(with: newModel.molecule, delegateObject), + let currentJson = self.topAlertObject?.json, + let currentModel = decodeTopNotification(with: currentJson, delegateObject: delegateObject), + let currentModelName = MoleculeObjectMapping.shared()?.getMoleculeClass(currentModel.molecule)?.nameForReuse(with: currentModel.molecule, delegateObject), + newModelName == currentModelName, + let molecule = currentAlert as? MoleculeViewProtocol else { return /* Something is not right, revisit */} + // Update molecule + MVMCoreDispatchUtility.performBlock(onMainThread: { + molecule.reset() + molecule.set(with: newModel.molecule, delegateObject, nil) + (molecule as? MVMCoreViewProtocol)?.updateView(self.bounds.width) + + // Update status bar. + guard let statusBarDelegate = molecule as? StatusBarUI else { return } + let statusBarUI = statusBarDelegate.getStatusBarUI() + self.setStatusBarColor(statusBarUI.color, statusBarStyle: statusBarUI.style) + }) + } + /// Returns the top alert molecule to use and status bar color legacy style. @objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer?, statusBarStyle: UnsafeMutablePointer?) -> UIView? { do { diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h index 82c0a7e9..1979a850 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h @@ -27,6 +27,9 @@ // Current top alert object @property (strong, nullable, nonatomic) MVMCoreTopAlertObject *topAlertObject; +/// Current top alert view. +@property (weak, nullable, nonatomic, readonly) UIView *currentAlert; + // Returns the top alert view + (nullable instancetype)sharedGlobal; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index d54190b0..14f4dee2 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -39,7 +39,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; @property (strong, nonatomic) NSLayoutConstraint *statusBarBottomConstraint; @property (weak, nonatomic) UIView *alertView; -@property (weak, nonatomic) UIView *currentAlert; +@property (weak, nullable, nonatomic, readwrite) UIView *currentAlert; @property (strong, nonatomic) NSLayoutConstraint *height; @property (weak, nonatomic) MVMCoreUITopAlertExpandableView *topAlertClearspotView; @@ -181,6 +181,10 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; }); } +- (void)updateTopAlertWith:(MVMCoreTopAlertObject *)topAlertObject { + [self updateMoleculeWith:topAlertObject]; +} + - (void)setStatusBarColor:(nullable UIColor *)statusBarColor statusBarStyle:(UIStatusBarStyle)style { self.statusBarView.backgroundColor = statusBarColor; self.statusBarStyle = style; From 44c74765425cab5444cef34d26b46235f6ec1331 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 15 Oct 2020 16:51:27 -0400 Subject: [PATCH 31/31] clean --- .../TopAlert/MVMCoreUITopAlertView+Extension.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 5c199c14..6a3c78de 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -40,6 +40,7 @@ public extension MVMCoreUITopAlertView { MVMCoreAlertHandler.shared()?.checkPagesDependency(for: controller.pageType) } + /// Decodes the json into a TopNotificationModel func decodeTopNotification(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) -> TopNotificationModel? { do { return try TopNotificationModel.decode(json: json, delegateObject: delegateObject) @@ -57,6 +58,7 @@ public extension MVMCoreUITopAlertView { MVMCoreAlertHandler.shared()?.showTopAlert(with: object) } + /// Updates the current top alert molecule with the new object @objc func updateMolecule(with topAlertObject: MVMCoreTopAlertObject) { guard topAlertObject.type == self.topAlertObject?.type else { return } let delegateObject = getDelegateObject() @@ -67,9 +69,15 @@ public extension MVMCoreUITopAlertView { let currentModel = decodeTopNotification(with: currentJson, delegateObject: delegateObject), let currentModelName = MoleculeObjectMapping.shared()?.getMoleculeClass(currentModel.molecule)?.nameForReuse(with: currentModel.molecule, delegateObject), newModelName == currentModelName, - let molecule = currentAlert as? MoleculeViewProtocol else { return /* Something is not right, revisit */} - // Update molecule + let molecule = currentAlert as? MoleculeViewProtocol else { + // Log that we couldn't update. + if let errorObject = MVMCoreErrorObject(title: nil, message: nil, messageToLog: nil, code: ErrorCode.parsingJSON.rawValue, domain: ErrorDomainNative, location: "TopNotification update \(String(describing: topAlertObject.type))") { + MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + } + return + } MVMCoreDispatchUtility.performBlock(onMainThread: { + // Update molecule molecule.reset() molecule.set(with: newModel.molecule, delegateObject, nil) (molecule as? MVMCoreViewProtocol)?.updateView(self.bounds.width)