From 8b0d208047f66d01eba28c4a04244876c52c126c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 2 Dec 2019 15:59:44 -0500 Subject: [PATCH 01/51] Current solution to widow word. --- MVMCoreUI/Atoms/Views/Label.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 8474d74a..d677cf4b 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -685,13 +685,20 @@ extension Label { /// Converts the entire text into a link. All characters will be underlined and the intrinsic bounds will respond to tap. @objc public func makeTextButton(actionBlock: @escaping ActionBlock) { - + setTextLinkState(range: getRange, actionBlock: actionBlock) } /// Underlines the tappable region and stores the tap logic for interation. private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) { + if clauses.isEmpty { + // Replace the last whitespace with \u{00A0} No-break space. + if let start = text?.lastIndex(of: " "), let end = text?.index(after: start) { + text?.replaceSubrange(start.. Date: Wed, 4 Dec 2019 15:52:18 -0500 Subject: [PATCH 02/51] This needs to be broken down and understood, but the code is here and at least functional. --- MVMCoreUI/Atoms/Views/Label.swift | 61 ++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index d677cf4b..544f7666 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -695,7 +695,7 @@ extension Label { if clauses.isEmpty { // Replace the last whitespace with \u{00A0} No-break space. if let start = text?.lastIndex(of: " "), let end = text?.index(after: start) { - text?.replaceSubrange(start.. [Any] { + + if lineBreakMode != .byWordWrapping { + lineBreakMode = .byWordWrapping + } + + var lines = [Any]() /* capacity: 10 */ + let wordSeparators = CharacterSet.whitespacesAndNewlines + var currentLine: String? = self.text + let textLength: Int = (self.text?.count ?? 0) + var rCurrentLine = NSRange(location: 0, length: textLength) + var rWhitespace = NSRange(location: 0, length: 0) + var rRemainingText = NSRange(location: 0, length: textLength) + var done: Bool = false + + while !done { + // determine the next whitespace word separator position + rWhitespace.location = rWhitespace.location + rWhitespace.length + rWhitespace.length = textLength - rWhitespace.location + rWhitespace = (self.text! as NSString).rangeOfCharacter(from: wordSeparators, options: .caseInsensitive, range: rWhitespace) + if rWhitespace.location == NSNotFound { + rWhitespace.location = textLength + done = true + } + let rTest = NSRange(location: rRemainingText.location, length: rWhitespace.location - rRemainingText.location) + let textTest: String = (self.text! as NSString).substring(with: rTest) + let fontAttributes: [String: Any]? = [NSAttributedString.Key.font.rawValue: font] + let maxWidth = (textTest as NSString).size(withAttributes: [NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue): font]).width + if maxWidth > self.bounds.size.width { + lines.append(currentLine?.trimmingCharacters(in: wordSeparators) ?? "") + rRemainingText.location = rCurrentLine.location + rCurrentLine.length + rRemainingText.length = textLength - rRemainingText.location + continue + } + rCurrentLine = rTest + currentLine = textTest + } + + lines.append(currentLine?.trimmingCharacters(in: wordSeparators) ?? "") + + return lines + } + + open var lastLineWidth: CGFloat { + + let lines: [Any] = self.getSeparatedLines() + + if !lines.isEmpty { + let lastLine: String = (lines.last as? String)! + let fontAttributes = [NSAttributedString.Key.font.rawValue: font] + return (lastLine as NSString).size(withAttributes: [NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue): font]).width + } + + return 0 + } +} + // MARK: - extension UITapGestureRecognizer { From 1cb2899bb06de48777f870942db5c9cf5a4a3d48 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 19 Nov 2020 09:17:49 -0500 Subject: [PATCH 03/51] excluding architecture arm64 --- MVMCoreUI.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 3fce1455..f090183b 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -2957,6 +2957,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -3022,6 +3023,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; From 83d0786597861f9990bc6e44d0ee0f7b8d954f0c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 2 Dec 2020 11:40:36 -0500 Subject: [PATCH 04/51] updates made. --- .../Atoms/Views/Label/LabelAttributeModel.swift | 12 ++++-------- .../ModelProtocols/MoleculeModelProtocol.swift | 12 +++++++++--- MVMCoreUI/BaseClasses/View.swift | 14 +++++++------- MVMCoreUI/Behaviors/PageBehavior.swift | 11 ++++------- .../FormUIHelpers/Rules/Rules/RulesProtocol.swift | 8 +++----- 5 files changed, 27 insertions(+), 30 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift index ae5f15b7..4c456a67 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift @@ -13,19 +13,15 @@ //-------------------------------------------------- public static var categoryName: String { - return "\(LabelAttributeModel.self)" + "\(LabelAttributeModel.self)" } - public static var categoryCodingKey: String { - return "type" - } + public static var categoryCodingKey: String { "type" } - public class var identifier: String { - return "" - } + public class var identifier: String { "" } var type: String { - get { return Self.identifier } + get { Self.identifier } } var location: Int diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift index dda19b35..1fdc22ad 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift @@ -10,19 +10,25 @@ public enum MolecularError: Swift.Error { public protocol MoleculeModelProtocol: ModelProtocol { var moleculeName: String { get } var backgroundColor: Color? { get set } + var accessibilityIdentifier: String? { get set } } public extension MoleculeModelProtocol { var moleculeName: String { - get { return Self.identifier } + get { Self.identifier } + } + + var accessibilityIdentifier: String? { + get { nil } + set { } } static var categoryName: String { - return "\(MoleculeModelProtocol.self)" + "\(MoleculeModelProtocol.self)" } static var categoryCodingKey: String { - return "moleculeName" + "moleculeName" } } diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index cfb6ff1f..688fe28c 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -58,6 +58,10 @@ import UIKit if let backgroundColor = model.backgroundColor { self.backgroundColor = backgroundColor.uiColor } + + if let identifier = model.accessibilityIdentifier { + accessibilityIdentifier = identifier + } } open func reset() { @@ -67,16 +71,12 @@ import UIKit // 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 + model.moleculeName } - open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return nil - } + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil } - open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - return nil - } + open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { nil } } // MARK:- MVMCoreViewProtocol diff --git a/MVMCoreUI/Behaviors/PageBehavior.swift b/MVMCoreUI/Behaviors/PageBehavior.swift index d8fd99a3..9e52f778 100644 --- a/MVMCoreUI/Behaviors/PageBehavior.swift +++ b/MVMCoreUI/Behaviors/PageBehavior.swift @@ -10,23 +10,22 @@ import Foundation public protocol PageBehaviorProtocol: ModelProtocol { - // The type of rule + /// The type of rule var behaviorName: String { get } - } public extension PageBehaviorProtocol { var behaviorName: String { - get { return Self.identifier } + get { Self.identifier } } static var categoryCodingKey: String { - return "behaviorName" + "behaviorName" } static var categoryName: String { - return "\(PageBehaviorProtocol.self)" + "\(PageBehaviorProtocol.self)" } } @@ -34,11 +33,9 @@ public protocol PageVisibilityBehavior: PageBehaviorProtocol { func onPageShown() func onPageHidden() - } public protocol PageBehaviorsTemplateProtocol { var behaviors: [PageBehaviorProtocol]? { get } - } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift index 7392ea8f..04259dbc 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift @@ -32,15 +32,13 @@ public protocol RulesProtocol: ModelProtocol { public extension RulesProtocol { var type: String { - get { return Self.identifier } + get { Self.identifier } } - static var categoryCodingKey: String { - return "type" - } + static var categoryCodingKey: String { "type" } static var categoryName: String { - return "\(RulesProtocol.self)" + "\(RulesProtocol.self)" } // Individual rule can override the function to validate based on the rule type. From acebc936d76aa494c39fc73a20272156a0c9f166 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 2 Dec 2020 12:50:42 -0500 Subject: [PATCH 05/51] simplifying. --- .../ModelProtocols/MoleculeModelProtocol.swift | 12 +++--------- .../FormUIHelpers/Rules/Rules/RulesProtocol.swift | 8 ++------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift index 1fdc22ad..e554581d 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift @@ -15,20 +15,14 @@ public protocol MoleculeModelProtocol: ModelProtocol { public extension MoleculeModelProtocol { - var moleculeName: String { - get { Self.identifier } - } + var moleculeName: String { Self.identifier } var accessibilityIdentifier: String? { get { nil } set { } } - static var categoryName: String { - "\(MoleculeModelProtocol.self)" - } + static var categoryName: String { "\(MoleculeModelProtocol.self)" } - static var categoryCodingKey: String { - "moleculeName" - } + static var categoryCodingKey: String { "moleculeName" } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift index 04259dbc..5a8b47a8 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift @@ -31,15 +31,11 @@ public protocol RulesProtocol: ModelProtocol { public extension RulesProtocol { - var type: String { - get { Self.identifier } - } + var type: String { Self.identifier } static var categoryCodingKey: String { "type" } - static var categoryName: String { - "\(RulesProtocol.self)" - } + static var categoryName: String { "\(RulesProtocol.self)" } // Individual rule can override the function to validate based on the rule type. func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { From 1a60d36e2d4094364a17d2cd489f8e6a71a3eacb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 3 Dec 2020 14:22:06 -0500 Subject: [PATCH 06/51] formatting updates. Adding identifier for easier testing. --- MVMCoreUI.xcodeproj/project.pbxproj | 2 +- .../Atomic/Atoms/Buttons/ButtonModel.swift | 4 ++ .../Atomic/Atoms/Buttons/CaretLink.swift | 13 ++----- .../Atomic/Atoms/Buttons/CaretLinkModel.swift | 4 ++ .../Atomic/Atoms/Buttons/PillButton.swift | 10 ++--- .../Atomic/Atoms/Views/DashLineModel.swift | 4 ++ .../Atomic/Atoms/Views/MultiProgress.swift | 7 ++-- .../HorizontalCombinationViews/TabBar.swift | 14 ++----- ...reUITopAlertExpandableView+Extension.swift | 7 +++- .../MVMCoreUITopAlertMainView+Extension.swift | 11 +++++- .../TopNotification/NotificationModel.swift | 39 ++++++++++++++----- MVMCoreUI/Atomic/Organisms/Stack.swift | 2 +- MVMCoreUI/BaseClasses/BarButtonItem.swift | 3 +- MVMCoreUI/BaseClasses/Button.swift | 18 ++++----- MVMCoreUI/BaseClasses/Control.swift | 17 ++++---- MVMCoreUI/BaseClasses/ImageView.swift | 25 ++++++------ .../BaseClasses/SectionHeaderFooterView.swift | 17 ++++---- MVMCoreUI/BaseClasses/TableView.swift | 2 +- MVMCoreUI/BaseClasses/TextField.swift | 4 ++ MVMCoreUI/BaseClasses/TextView.swift | 4 +- MVMCoreUI/BaseClasses/View.swift | 4 +- 21 files changed, 122 insertions(+), 89 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index f090183b..e97e3b20 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -2154,6 +2154,7 @@ D2B18B7D236090D500A9AEDC /* BaseClasses */ = { isa = PBXGroup; children = ( + 0A5D59C323AD488600EFD9E9 /* Protocols */, 0A6682B3243769C700AD3CA1 /* TextView.swift */, C003506023AA94CD00B6AC29 /* Button.swift */, D2B18B7E2360913400A9AEDC /* Control.swift */, @@ -2164,7 +2165,6 @@ BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */, D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */, D264FAA92440F97600D98315 /* CollectionView.swift */, - 0A5D59C323AD488600EFD9E9 /* Protocols */, 0A7918F423F5E7EA00772FF4 /* ImageView.swift */, D272F5F82473163100BD1A8F /* BarButtonItem.swift */, D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift index 0881af71..9662fc48 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift @@ -18,6 +18,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW public static var identifier: String = "button" public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var title: String public var action: ActionModelProtocol public var enabled: Bool = true @@ -172,6 +173,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case title case inverted case action @@ -195,6 +197,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) title = try typeContainer.decode(String.self, forKey: .title) action = try typeContainer.decodeModel(codingKey: .action) @@ -252,6 +255,7 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW try container.encode(inverted, forKey: .inverted) try container.encodeModel(action, forKey: .action) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(enabledFillColor, forKey: .fillColor) try container.encodeIfPresent(enabledTextColor, forKey: .textColor) try container.encodeIfPresent(enabledBorderColor, forKey: .borderColor) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift index b0822480..5ae68241 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift @@ -127,7 +127,6 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { } public func updateCaretSpacing(_ spacing: CGFloat) { - caretSpacingConstraint?.constant = spacing } @@ -151,15 +150,9 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { setTitle(model.title, for: .normal) } - public func needsToBeConstrained() -> Bool { - return true - } + public func needsToBeConstrained() -> Bool { true } - open func horizontalAlignment() -> UIStackView.Alignment { - return .leading - } + open func horizontalAlignment() -> UIStackView.Alignment { .leading } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 10.5 - } + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 10.5 } } diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift index a37baeca..25d0a539 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift @@ -17,6 +17,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea public static var identifier: String = "caretLink" public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var title: String public var action: ActionModelProtocol public var enabledColor: Color = Color(uiColor: .mvmBlack) @@ -41,6 +42,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea private enum CodingKeys: String, CodingKey { case backgroundColor + case accessibilityIdentifier case title case action case enabledColor_inverted @@ -60,6 +62,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) title = try typeContainer.decode(String.self, forKey: .title) if let enabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor_inverted) { @@ -94,6 +97,7 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol, Enablea try container.encode(moleculeName, forKey: .moleculeName) try container.encode(title, forKey: .title) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeModel(action, forKey: .action) try container.encode(enabled, forKey: .enabledColor) try container.encodeIfPresent(disabledColor, forKey: .disabledColor) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index f5a7ed44..b89d034d 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -18,7 +18,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { var size = MVMCoreUIUtility.getWidth() var buttonModel: ButtonModel? { - get { return model as? ButtonModel } + get { model as? ButtonModel } } /// Need to re-style on set. @@ -47,12 +47,12 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { //-------------------------------------------------- public var enabledTitleColor: UIColor? { - get { return titleColor(for: .normal) } + get { titleColor(for: .normal) } set { setTitleColor(newValue, for: .normal) } } public var disabledTitleColor: UIColor? { - get { return titleColor(for: .disabled) } + get { titleColor(for: .disabled) } set { setTitleColor(newValue, for: .disabled) } } @@ -228,9 +228,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { // MARK: - MVMCoreUIViewConstrainingProtocol //-------------------------------------------------- - open func horizontalAlignment() -> UIStackView.Alignment { - return .center - } + open func horizontalAlignment() -> UIStackView.Alignment { .center } public func enableField(_ enable: Bool) { isEnabled = enable diff --git a/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift index 523d83a6..c4375d53 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift @@ -16,6 +16,7 @@ import Foundation public static var identifier: String = "dashLine" public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var dashColor: Color = Color(uiColor: .mvmCoolGray3) public var dashColor_inverted: Color = Color(uiColor: .mvmWhite) public var isHidden: Bool = false @@ -36,6 +37,7 @@ import Foundation private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case dashColor_inverted case dashColor case isHidden @@ -57,6 +59,7 @@ import Foundation } backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) if let isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden) { self.isHidden = isHidden @@ -69,5 +72,6 @@ import Foundation try container.encode(dashColor, forKey: .dashColor) try container.encode(isHidden, forKey: .isHidden) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift b/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift index 945229c7..2894ea1c 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift @@ -17,7 +17,7 @@ import UIKit private let stack = Stack() var multiProgressModel: MultiProgressBarModel? { - get { return model as? MultiProgressBarModel } + get { model as? MultiProgressBarModel } } var roundedCorners: Bool = false { @@ -85,7 +85,7 @@ import UIKit //-------------------------------------------------- /// Creates the bars - open func set(with progressList: Array, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + open func set(with progressList: [SingleProgressBarModel], _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { stack.removeAllItemViews() guard let stackModel = stack.stackModel else { return } @@ -103,7 +103,6 @@ import UIKit stack.set(with: stackModel, delegateObject, additionalData) } - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) @@ -115,6 +114,6 @@ import UIKit } public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return (model as? MultiProgressBarModel)?.thickness ?? 8 + (model as? MultiProgressBarModel)?.thickness ?? 8 } } diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift index 1ee7ee71..87e71631 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift @@ -6,7 +6,6 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate { @@ -15,9 +14,7 @@ import Foundation public let line = Line() required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let model = model as? TabBarModel else { - fatalError("model is not TabBarModel") - } + guard let model = model as? TabBarModel else { fatalError("model is not TabBarModel") } self.model = model super.init(frame: .zero) @@ -32,7 +29,7 @@ import Foundation fatalError("init(coder:) has not been implemented") } - open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let model = model as? TabBarModel else { return } self.model = model @@ -100,10 +97,7 @@ import Foundation }) } - public func currentTabIndex() -> Int { - return model.selectedTab - } + public func currentTabIndex() -> Int { model.selectedTab } } -extension UITabBarItem: MFButtonProtocol { -} +extension UITabBarItem: MFButtonProtocol { } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift index e28bf057..e1af54e3 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift @@ -6,11 +6,10 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol { - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { defaultSetup() guard let model = model as? CollapsableNotificationModel else { return } backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen @@ -30,6 +29,10 @@ extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol { MVMCoreUITopAlertBaseView.addAction(to: button, actionMap: topActionMap, additionalData: nil) shortView?.label?.accessibilityTraits = .button } + + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } } } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift index 157d6522..c6109372 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift @@ -6,17 +6,24 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation extension MVMCoreUITopAlertMainView: MoleculeViewProtocol { - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { defaultSetup() guard let model = model as? NotificationModel else { return } + backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen var actionMap = model.button?.action.toJSON() + if let title = model.button?.title { actionMap?.updateValue(title, forKey: KeyTitle) } + + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } + setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate) setup(withMessage: model.headline.text, subMessage: model.body?.text, color: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil) } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift index 70ff00bb..3d02cecd 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift @@ -6,52 +6,72 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation open class NotificationModel: MoleculeModelProtocol { - public class var identifier: String { - return "notification" - } + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public class var identifier: String { "notification" } + public var accessibilityIdentifier: String? public var backgroundColor: Color? public var headline: LabelModel public var body: LabelModel? public var button: ButtonModel? public var closeButton: NotificationXButtonModel? + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(with headline: LabelModel) { self.headline = headline } + //-------------------------------------------------- + // MARK: - Default + //-------------------------------------------------- + open func setDefault() { if backgroundColor == nil { - backgroundColor = Color(uiColor: .mvmGreen()) + backgroundColor = Color(uiColor: .mvmGreen) } if headline.textColor == nil { - headline.textColor = Color(uiColor: .white) + headline.textColor = Color(uiColor: .mvmWhite) } if body?.textColor == nil { - body?.textColor = Color(uiColor: .white) + body?.textColor = Color(uiColor: .mvmWhite) } if button?.style == nil { button?.style = .secondary } button?.size = .tiny - button?.enabledTextColor = Color(uiColor: .white) - button?.enabledBorderColor = Color(uiColor: .white) + button?.enabledTextColor = Color(uiColor: .mvmWhite) + button?.enabledBorderColor = Color(uiColor: .mvmWhite) } + //-------------------------------------------------- + // MARK: - Coding Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case headline case body case button case closeButton } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) headline = try typeContainer.decode(LabelModel.self, forKey: .headline) body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) @@ -63,6 +83,7 @@ open class NotificationModel: MoleculeModelProtocol { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(headline, forKey: .headline) try container.encodeIfPresent(body, forKey: .body) try container.encodeIfPresent(button, forKey: .button) diff --git a/MVMCoreUI/Atomic/Organisms/Stack.swift b/MVMCoreUI/Atomic/Organisms/Stack.swift index d112af5e..29646f2e 100644 --- a/MVMCoreUI/Atomic/Organisms/Stack.swift +++ b/MVMCoreUI/Atomic/Organisms/Stack.swift @@ -16,7 +16,7 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto open var stackItems: [UIView] = [] open var stackModel: T? { - get { return model as? T } + get { model as? T } } //-------------------------------------------------- diff --git a/MVMCoreUI/BaseClasses/BarButtonItem.swift b/MVMCoreUI/BaseClasses/BarButtonItem.swift index 93eb63a3..d41fd57f 100644 --- a/MVMCoreUI/BaseClasses/BarButtonItem.swift +++ b/MVMCoreUI/BaseClasses/BarButtonItem.swift @@ -8,6 +8,7 @@ public typealias BarButtonAction = (BarButtonItem) -> () + @objc class ActionDelegate: NSObject { var buttonAction: BarButtonAction? @objc func callActionBlock(_ sender: BarButtonItem) { @@ -16,7 +17,6 @@ public typealias BarButtonAction = (BarButtonItem) -> () } @objcMembers open class BarButtonItem: UIBarButtonItem, MFButtonProtocol { - //-------------------------------------------------- // MARK: - Delegate //-------------------------------------------------- @@ -43,4 +43,3 @@ public typealias BarButtonAction = (BarButtonItem) -> () } } } - diff --git a/MVMCoreUI/BaseClasses/Button.swift b/MVMCoreUI/BaseClasses/Button.swift index e06a5cb3..3387913e 100644 --- a/MVMCoreUI/BaseClasses/Button.swift +++ b/MVMCoreUI/BaseClasses/Button.swift @@ -65,7 +65,7 @@ public typealias ButtonAction = (Button) -> () /// Adds a block to be performed for the given event. open func addActionBlock(event: Event, _ buttonBlock: @escaping ButtonAction) { self.buttonAction = buttonBlock - addTarget(self, action: #selector(callActionBlock(_:)), for: event) + addTarget(self, action: #selector(callActionBlock), for: event) } @objc func callActionBlock(_ sender: Button) { @@ -103,6 +103,10 @@ public typealias ButtonAction = (Button) -> () self.backgroundColor = backgroundColor.uiColor } + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } + if let model = model as? EnableableModelProtocol { isEnabled = model.enabled } @@ -119,16 +123,12 @@ public typealias ButtonAction = (Button) -> () // 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 + model.moleculeName } - open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return nil - } + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil } - open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - return nil - } + open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { nil } //-------------------------------------------------- // MARK: - Accessibility @@ -160,6 +160,6 @@ extension Button: MVMCoreViewProtocol { extension Button: AppleGuidelinesProtocol { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - return Self.acceptablyOutsideBounds(point: point, bounds: bounds) + Self.acceptablyOutsideBounds(point: point, bounds: bounds) } } diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index 57c13e9e..a8fde2e5 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -51,9 +51,14 @@ import UIKit // MARK:- MoleculeViewProtocol open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.model = model + if let backgroundColor = model.backgroundColor { self.backgroundColor = backgroundColor.uiColor } + + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } } open func reset() { @@ -63,22 +68,18 @@ import UIKit // 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 + model.moleculeName } - open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return nil - } + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil } - open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - return nil - } + open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { nil } } // MARK: - AppleGuidelinesProtocol extension Control: AppleGuidelinesProtocol { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - return Self.acceptablyOutsideBounds(point: point, bounds: bounds) + Self.acceptablyOutsideBounds(point: point, bounds: bounds) } } diff --git a/MVMCoreUI/BaseClasses/ImageView.swift b/MVMCoreUI/BaseClasses/ImageView.swift index 271ee615..9aa003b8 100644 --- a/MVMCoreUI/BaseClasses/ImageView.swift +++ b/MVMCoreUI/BaseClasses/ImageView.swift @@ -53,33 +53,35 @@ open class ImageView: UIImageView, MoleculeViewProtocol { } public func reset() { - backgroundColor = .clear - } - + backgroundColor = .clear + } + public func setAsMolecule() { } //-------------------------------------------------- // MARK: - ModelMoleculeViewProtocol //-------------------------------------------------- - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.model = model + if let backgroundColor = model.backgroundColor { self.backgroundColor = backgroundColor.uiColor } + + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } } open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - return model?.moleculeName + model?.moleculeName } - open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return nil - } + open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil } - open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - return nil - } + open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { nil } } // MARK:- MVMCoreViewProtocol @@ -94,4 +96,3 @@ extension ImageView: MVMCoreViewProtocol { MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0) } } - diff --git a/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift b/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift index b4b7f8eb..ab85e96f 100644 --- a/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift +++ b/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift @@ -6,7 +6,6 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers open class SectionHeaderFooterView: UITableViewHeaderFooterView, MoleculeViewProtocol { //-------------------------------------------------- @@ -43,10 +42,16 @@ import Foundation //-------------------------------------------------- open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.model = model + if let backgroundColor = model.backgroundColor { contentView.backgroundColor = backgroundColor.uiColor } + + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } } open func reset() { @@ -56,16 +61,12 @@ import Foundation // 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 + model.moleculeName } - open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return nil - } + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { nil } - open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - return nil - } + open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { nil } } // MARK:- MVMCoreViewProtocol diff --git a/MVMCoreUI/BaseClasses/TableView.swift b/MVMCoreUI/BaseClasses/TableView.swift index 9fb07ac7..bc899084 100644 --- a/MVMCoreUI/BaseClasses/TableView.swift +++ b/MVMCoreUI/BaseClasses/TableView.swift @@ -11,7 +11,7 @@ import Foundation @objcMembers open class TableView: UITableView { /// A block that gets called on tableview frame changes - public var frameChangeAction: (() -> Void)? + public var frameChangeAction: (() -> ())? private var previousFrame = CGRect.zero diff --git a/MVMCoreUI/BaseClasses/TextField.swift b/MVMCoreUI/BaseClasses/TextField.swift index 8795054b..9174426d 100644 --- a/MVMCoreUI/BaseClasses/TextField.swift +++ b/MVMCoreUI/BaseClasses/TextField.swift @@ -93,6 +93,10 @@ extension TextField: MoleculeViewProtocol { if let color = model.backgroundColor?.uiColor { backgroundColor = color } + + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } } open func reset() { diff --git a/MVMCoreUI/BaseClasses/TextView.swift b/MVMCoreUI/BaseClasses/TextView.swift index 2c609e02..8d09ca35 100644 --- a/MVMCoreUI/BaseClasses/TextView.swift +++ b/MVMCoreUI/BaseClasses/TextView.swift @@ -106,8 +106,8 @@ import UIKit open func reset() { - fontStyle = Styler.Font.RegularBodyLarge - placeholderFontStyle = Styler.Font.RegularMicro + fontStyle = .RegularBodyLarge + placeholderFontStyle = .RegularMicro placeholderTextColor = .mvmCoolGray3 textColor = .mvmBlack isEnabled = true diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 688fe28c..deb1bdfa 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -59,8 +59,8 @@ import UIKit self.backgroundColor = backgroundColor.uiColor } - if let identifier = model.accessibilityIdentifier { - accessibilityIdentifier = identifier + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier } } From f3febee9034529ebdf74034ac2bd8aaf3c3f35c1 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 3 Dec 2020 16:45:14 -0500 Subject: [PATCH 07/51] exposing identifiers. implementing feature to change initial page. --- MVMCoreUI/BaseClasses/Button.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/BaseClasses/Button.swift b/MVMCoreUI/BaseClasses/Button.swift index 3387913e..79a72d02 100644 --- a/MVMCoreUI/BaseClasses/Button.swift +++ b/MVMCoreUI/BaseClasses/Button.swift @@ -8,6 +8,7 @@ public typealias ButtonAction = (Button) -> () + @objcMembers open class Button: UIButton, MFButtonProtocol, MoleculeViewProtocol { //-------------------------------------------------- // MARK: - Properties From 3d5c7bbf4cddc4080b32d7a9e11b96925af44d46 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 7 Dec 2020 17:08:53 -0500 Subject: [PATCH 08/51] new json. test cases. changes made. --- .../Molecules/HeadersAndFooters/Header.swift | 5 ++- .../MoleculeHeaderModel.swift | 1 - .../MoleculeHeaderView.swift | 2 +- .../Buttons/ImageBarButtonItem.swift | 7 ++--- .../Buttons/LabelBarButtonItem.swift | 2 -- .../Buttons/NavigationImageButtonModel.swift | 30 ++++++++++++++++-- .../Buttons/NavigationLabelButtonModel.swift | 26 ++++++++++++++-- .../NavigationBar/NavigationItemModel.swift | 31 +++++++++++++------ 8 files changed, 78 insertions(+), 26 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift index 63a13fbc..6989a15e 100644 --- a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift @@ -6,7 +6,6 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation open class HeaderView: Container { public let line = Line() @@ -15,7 +14,7 @@ open class HeaderView: Container { open var molecule: MoleculeViewProtocol? var headerModel: HeaderModel? { - get { return model as? HeaderModel } + get { model as? HeaderModel } } /// Convenience function to add a molecule to the view. @@ -58,6 +57,6 @@ open class HeaderView: Container { } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing } } diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift index b8f2144e..7b62cb1f 100644 --- a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift @@ -6,7 +6,6 @@ // Copyright © 2019 Suresh, Kamlesh. All rights reserved. // -import Foundation @objcMembers public class MoleculeHeaderModel: HeaderModel, MoleculeModelProtocol, MoleculeContainerModelProtocol { public static var identifier: String = "header" diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift index 291540af..9e4afed6 100644 --- a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift @@ -17,7 +17,7 @@ public class MoleculeHeaderView: MoleculeContainer { var line = Line() var headerModel: MoleculeHeaderModel? { - get { return model as? MoleculeHeaderModel } + get { model as? MoleculeHeaderModel } } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift index 564b9f42..33ed39b8 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift @@ -6,17 +6,15 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers open class ImageBarButtonItem: BarButtonItem { - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public static func create(with image: UIImage?) -> Self { let actionObject = ActionDelegate() - let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock(_:))) + let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock)) button.actionDelegate = actionObject return button } @@ -29,7 +27,7 @@ import Foundation } /// Creates the item with the passed in action map. - public static func create(with image: UIImage?, actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { + public static func create(with image: UIImage?, actionMap: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { let button = create(with: image) button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData) return button @@ -42,4 +40,3 @@ import Foundation return button } } - diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift index 989148d1..5175defe 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift @@ -6,10 +6,8 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers open class LabelBarButtonItem: BarButtonItem { - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift index 9313f9a4..77a5c350 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift @@ -5,28 +5,45 @@ // Created by Scott Pfeil on 5/18/20. // -import Foundation public class NavigationImageButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var backgroundColor: Color? + public var accessibilityIdentifier: String? public static var identifier: String = "navigationImageButton" - public var image: String public var action: ActionModelProtocol + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(with image: String, action: ActionModelProtocol) { self.image = image self.action = action } + //-------------------------------------------------- + // MARK: - Coding Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case image case action + case accessibilityIdentifier case moleculeName } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) image = try typeContainer.decode(String.self, forKey: .image) action = try typeContainer.decodeModel(codingKey: .action) } @@ -34,13 +51,20 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(image, forKey: .image) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeModel(action, forKey: .action) } + //-------------------------------------------------- + // MARK: - Method + //-------------------------------------------------- + /// Convenience function that creates a BarButtonItem for the model. public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image) - return ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) + let buttonItem = ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) + buttonItem.accessibilityIdentifier = accessibilityIdentifier ?? image + return buttonItem } } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift index 8d9f85ae..e52ea0d5 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift @@ -6,28 +6,45 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation public class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var backgroundColor: Color? public static var identifier: String = "navigationLabelButton" - + public var accessibilityIdentifier: String? public var title: String public var action: ActionModelProtocol + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(with title: String, action: ActionModelProtocol) { self.title = title self.action = action } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName + case accessibilityIdentifier case title case action } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) title = try typeContainer.decode(String.self, forKey: .title) action = try typeContainer.decodeModel(codingKey: .action) } @@ -35,10 +52,15 @@ public class NavigationLabelButtonModel: NavigationButtonModelProtocol, Molecule open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(title, forKey: .title) try container.encodeModel(action, forKey: .action) } + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + /// Convenience function that creates a BarButtonItem for the model. public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { return LabelBarButtonItem.create(with: title, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index 15ef632e..5fab61f9 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -6,12 +6,13 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { - open class var identifier: String { - return "navigationBar" - } + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + open class var identifier: String { "navigationBar" } open var title: String? open var hidden: Bool @@ -28,13 +29,21 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc open var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? open var titleView: MoleculeModelProtocol? + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init() { hidden = false - backgroundColor = Color(uiColor: .white) - tintColor = Color(uiColor: .black) + backgroundColor = Color(uiColor: .mvmWhite) + tintColor = Color(uiColor: .mvmBlack) line = LineModel(type: .standard) } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case title @@ -48,13 +57,17 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc case additionalRightButtons case titleView } - + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) title = try typeContainer.decodeIfPresent(String.self, forKey: .title) hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) - tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .mvmWhite) + tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .mvmBlack) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard) alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton) From 88110b6508c8aa7a985b09098cb0b28f3b655747 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 9 Dec 2020 14:58:46 -0500 Subject: [PATCH 09/51] formatting improvement. testing logic. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 ++-- MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift | 9 +++------ MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift | 4 ++++ .../TextFields/BaseDropdownEntryFieldModel.swift | 4 +--- .../TextFields/DateDropdownEntryField.swift | 4 ++-- .../TextFields/DateDropdownEntryFieldModel.swift | 4 +--- .../FormFields/TextFields/DigitEntryFieldModel.swift | 4 +--- .../Atoms/FormFields/TextFields/EntryField.swift | 4 +--- .../Atoms/FormFields/TextFields/EntryFieldModel.swift | 8 +++++--- .../TextFields/ItemDropdownEntryField.swift | 6 ++---- .../FormFields/TextFields/MdnEntryFieldModel.swift | 4 +--- .../Atoms/FormFields/TextFields/TextEntryField.swift | 11 +++++------ .../FormFields/TextFields/TextEntryFieldModel.swift | 4 +--- 13 files changed, 29 insertions(+), 41 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index e97e3b20..9194a70a 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -2024,10 +2024,10 @@ D29DF22B21E6A0FA003B2FB9 /* TextFields */ = { isa = PBXGroup; children = ( - 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */, - 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */, 0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */, 0A21DB7E235DECC500C160A2 /* EntryField.swift */, + 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */, + 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */, 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */, 0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */, 0A8321AE2355FE9500CB7F00 /* DigitBox.swift */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift index d484840d..362d646f 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift @@ -52,15 +52,14 @@ import UIKit setTitle(model.title, for: .normal) accessibilityLabel = model.title + accessibilityIdentifier = model.accessibilityIdentifier setTitleColor((model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor, for: .normal) setTitleColor((model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor, for: .disabled) isEnabled = model.enabled set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 31 - } + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 31 } } // MARK: - MVMCoreViewProtocol @@ -98,7 +97,5 @@ extension Link { // MARK: - MVMCoreUIViewConstrainingProtocol extension Link: MVMCoreUIViewConstrainingProtocol { - open func horizontalAlignment() -> UIStackView.Alignment { - return .leading - } + open func horizontalAlignment() -> UIStackView.Alignment { .leading } } diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift index bd1b601d..4519b030 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift @@ -19,6 +19,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode } public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var title: String public var action: ActionModelProtocol public var enabled = true @@ -44,6 +45,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case title case action case enabled @@ -62,6 +64,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) title = try typeContainer.decode(String.self, forKey: .title) action = try typeContainer.decodeModel(codingKey: .action) @@ -95,6 +98,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode try container.encode(title, forKey: .title) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeModel(action, forKey: .action) try container.encode(inverted, forKey: .inverted) try container.encode(enabled, forKey: .enabled) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift index 0bb17284..f256544c 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift @@ -14,9 +14,7 @@ public var caretView: CaretViewModel? public var action: ActionModelProtocol? - public override class var identifier: String { - return "" - } + public override class var identifier: String { "" } //-------------------------------------------------- // MARK: - Keys diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift index 6ff950f6..c1ea8080 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift @@ -23,15 +23,15 @@ import UIKit }() public var dateFormat: String? { + get { dateDropdownModel?.dateFormat } set { guard let newValue = newValue else { return } dateDropdownModel?.dateFormat = newValue } - get { return dateDropdownModel?.dateFormat } } public var dateDropdownModel: DateDropdownEntryFieldModel? { - return model as? DateDropdownEntryFieldModel + model as? DateDropdownEntryFieldModel } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryFieldModel.swift index 0ac448a2..53c7206c 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryFieldModel.swift @@ -11,9 +11,7 @@ // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { - return "dateDropdownEntryField" - } + public override class var identifier: String { "dateDropdownEntryField" } public var dateFormatter: DateFormatter = { let formatter = DateFormatter() diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryFieldModel.swift index 4132a15e..8003c1b0 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryFieldModel.swift @@ -12,9 +12,7 @@ // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { - return "digitTextField" - } + public override class var identifier: String { "digitTextField" } public var digits: Int = 4 diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift index 65b13861..dc4e15de 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift @@ -327,9 +327,7 @@ import UIKit } } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 115 - } + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 115 } } // MARK: - Accessibility diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift index d64f70ae..b7088c96 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift @@ -19,6 +19,7 @@ import Foundation } public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var title: String? public var feedback: String? public var errorMessage: String? @@ -48,6 +49,7 @@ import Foundation private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case title case enabled case feedback @@ -66,9 +68,7 @@ import Foundation // MARK: - Validation Methods //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { - return text - } + public func formFieldValue() -> AnyHashable? { text } public func setValidity(_ valid: Bool, rule: RulesProtocol) { @@ -96,6 +96,7 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) title = try typeContainer.decodeIfPresent(String.self, forKey: .title) feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback) errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage) @@ -117,6 +118,7 @@ import Foundation var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(title, forKey: .title) try container.encodeIfPresent(feedback, forKey: .feedback) try container.encodeIfPresent(text, forKey: .text) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift index 47c59dae..9002d701 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift @@ -120,12 +120,10 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { // MARK:- Base Picker Delegate extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { - @objc public func numberOfComponents(in pickerView: UIPickerView) -> Int { - return 1 - } + @objc public func numberOfComponents(in pickerView: UIPickerView) -> Int { 1 } @objc public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - return pickerData.count + pickerData.count } @objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryFieldModel.swift index 0541de8d..53d0703d 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryFieldModel.swift @@ -11,7 +11,5 @@ // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { - return "mdnEntryField" - } + public override class var identifier: String { "mdnEntryField" } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryField.swift index 2c4aa736..84a19fb5 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryField.swift @@ -54,9 +54,7 @@ import UIKit /// Validate when user resigns editing. Default: true public var validateWhenDoneEditing: Bool = true - public var textEntryFieldModel: TextEntryFieldModel? { - return model as? TextEntryFieldModel - } + public var textEntryFieldModel: TextEntryFieldModel? { model as? TextEntryFieldModel } //-------------------------------------------------- // MARK: - Computed Properties @@ -96,7 +94,7 @@ import UIKit /// The text of this TextField. open override var text: String? { - get { return textField.text } + get { textField.text } set { textField.text = newValue textEntryFieldModel?.text = newValue @@ -105,7 +103,7 @@ import UIKit /// Placeholder access for the TextField. public var placeholder: String? { - get { return textField.placeholder } + get { textField.placeholder } set { textField.placeholder = newValue } } @@ -133,7 +131,7 @@ import UIKit /// If you're using a ViewController, you must set this to it public weak var uiTextFieldDelegate: UITextFieldDelegate? { - get { return textField.delegate } + get { textField.delegate } set { textField.delegate = newValue } } @@ -330,6 +328,7 @@ import UIKit break } + textField.accessibilityIdentifier = model.accessibilityIdentifier uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate observingTextFieldDelegate = delegateObject?.observingTextFieldDelegate setupTextFieldToolbar() diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryFieldModel.swift index 6f54cb38..5bdc26cc 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextEntryFieldModel.swift @@ -25,9 +25,7 @@ // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { - return "textField" - } + public override class var identifier: String { "textField" } public var placeholder: String? public var enabledTextColor: Color = Color(uiColor: .mvmBlack) From 0eaa7c1613223e8ca26d75e4bbc4b9061d8f9aba Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 10 Dec 2020 14:28:55 -0500 Subject: [PATCH 10/51] exposing accessibility. test improvements. --- .../Atomic/Atoms/Buttons/ButtonModel.swift | 12 +++++----- .../Atomic/Atoms/Buttons/PillButton.swift | 10 +++++---- .../Atomic/Atoms/Selectors/Checkbox.swift | 22 +++++++------------ .../Atoms/Selectors/CheckboxModel.swift | 9 +++++--- .../Atomic/Atoms/Selectors/RadioButton.swift | 21 +++++++++--------- .../Atoms/Selectors/RadioButtonModel.swift | 9 ++++---- MVMCoreUI/BaseClasses/Button.swift | 2 ++ MVMCoreUI/FormUIHelpers/FormValidator.swift | 9 +++++--- 8 files changed, 50 insertions(+), 44 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift index 9662fc48..52ade755 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift @@ -94,27 +94,27 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW //-------------------------------------------------- public func enabled_fillColor() -> UIColor? { - return (inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor + (inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor } public func enabled_textColor() -> UIColor? { - return (inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor + (inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor } public func enabled_borderColor() -> UIColor? { - return (inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor + (inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor } public func disabled_fillColor() -> UIColor? { - return (inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor + (inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor } public func disabled_textColor() -> UIColor? { - return (inverted ? disabledTextColor_inverted : disabledTextColor)?.uiColor + (inverted ? disabledTextColor_inverted : disabledTextColor)?.uiColor } public func disabled_borderColor() -> UIColor? { - return (inverted ? disabledBorderColor_inverted : disabledBorderColor)?.uiColor + (inverted ? disabledBorderColor_inverted : disabledBorderColor)?.uiColor } /// Defines the default appearance for the primary style. diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index b89d034d..4a7a8481 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -27,9 +27,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { } open var buttonSize: Styler.Button.Size = .standard { - didSet { - buttonModel?.size = buttonSize - } + didSet { buttonModel?.size = buttonSize } } //-------------------------------------------------- @@ -106,6 +104,8 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { self.disabledTitleColor = disabledTitleColor } + isAccessibilityElement = isEnabled + if isEnabled { if let fillColor = buttonModel?.enabledColors.fill { backgroundColor = fillColor @@ -128,7 +128,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { } private func getInnerPadding() -> CGFloat { - return getHeight() / 2.0 + getHeight() / 2.0 } private func getHeight() -> CGFloat { @@ -181,9 +181,11 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { guard let model = model as? ButtonModel else { return } setTitle(model.title, for: .normal) + if let size = model.size { buttonSize = size } + model.updateUI = { [weak self] in MVMCoreDispatchUtility.performBlock(onMainThread: { self?.enableField(model.enabled) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index edc37628..b440adaa 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -25,7 +25,7 @@ import MVMCore var delegateObject: MVMCoreUIDelegateObject? public var checkboxModel: CheckboxModel? { - return model as? CheckboxModel + model as? CheckboxModel } public static let defaultHeightWidth: CGFloat = 18.0 @@ -59,7 +59,7 @@ import MVMCore /// Retrieves ideeal radius value to curve square into a circle. public var cornerRadiusValue: CGFloat { - return bounds.size.height / 2 + bounds.size.height / 2 } /// Action Block called when the switch is selected. @@ -102,23 +102,17 @@ import MVMCore /// Color of the check mark. public var checkColor: UIColor = .mvmBlack { - didSet { - setShapeLayerStrokeColor(checkColor) - } + didSet { setShapeLayerStrokeColor(checkColor) } } /// Border width of the checkbox public var borderWidth: CGFloat = 1 { - didSet { - layer.borderWidth = borderWidth - } + didSet { layer.borderWidth = borderWidth } } /// border color of the Checkbox public var borderColor: UIColor = .mvmBlack { - didSet { - layer.borderColor = borderColor.cgColor - } + didSet { layer.borderColor = borderColor.cgColor } } /** @@ -367,9 +361,7 @@ import MVMCore // MARK: - Molecular //-------------------------------------------------- - open func needsToBeConstrained() -> Bool { - return true - } + open func needsToBeConstrained() -> Bool { true } open override func reset() { super.reset() @@ -415,6 +407,8 @@ import MVMCore self.fieldKey = fieldKey } + accessibilityIdentifier = model.accessibilityIdentifier + borderColor = (model.inverted ? model.invertedColor : model.borderColor).uiColor borderWidth = model.borderWidth diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index bb21da1b..c2d9f14e 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -16,6 +16,7 @@ import Foundation public static var identifier: String = "checkbox" public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var checked: Bool = false public var enabled: Bool = true public var animated: Bool = true @@ -44,6 +45,7 @@ import Foundation private enum CodingKeys: String, CodingKey { case moleculeName + case accessibilityIdentifier case checked case enabled case inverted @@ -69,9 +71,7 @@ import Foundation // MARK: - Methods //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { - return checked - } + public func formFieldValue() -> AnyHashable? { checked } //-------------------------------------------------- // MARK: - Initializer @@ -89,6 +89,8 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) + if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) { self.borderWidth = borderWidth } @@ -169,6 +171,7 @@ import Foundation try container.encode(borderWidth, forKey: .borderWidth) try container.encode(checked, forKey: .checked) try container.encode(inverted, forKey: .inverted) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(checkColor, forKey: .checkColor) try container.encodeIfPresent(invertedColor, forKey: .invertedColor) try container.encodeIfPresent(invertedBackgroundColor, forKey: .invertedBackgroundColor) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index bd720be8..a22907ce 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -15,9 +15,7 @@ import UIKit //-------------------------------------------------- public var diameter: CGFloat = 30 { - didSet { - widthConstraint?.constant = diameter - } + didSet { widthConstraint?.constant = diameter } } @objc public override var isSelected: Bool { @@ -33,7 +31,7 @@ import UIKit var additionalData: [AnyHashable: Any]? public var radioModel: RadioButtonModel? { - return model as? RadioButtonModel + model as? RadioButtonModel } lazy public var radioGroupName: String? = { @@ -95,33 +93,34 @@ import UIKit if !isEnabled { return } + let wasPreviouslySelected = isSelected if let radioButtonModel = radioButtonSelectionHelper { radioButtonModel.selected(self) } else { isSelected = !isSelected } + if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected { Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel) } + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) setNeedsDisplay() } - public func isValidField() -> Bool { - return isSelected - } + public func isValidField() -> Bool { isSelected } public func formFieldName() -> String? { - return radioModel?.fieldKey + radioModel?.fieldKey } public func formFieldGroupName() -> String? { - return radioModel?.fieldKey + radioModel?.fieldKey } public func formFieldValue() -> AnyHashable? { - return radioModel?.fieldValue + radioModel?.fieldValue } //-------------------------------------------------- @@ -164,6 +163,8 @@ import UIKit guard let model = model as? RadioButtonModel else { return } + accessibilityIdentifier = model.accessibilityIdentifier + isSelected = model.state isEnabled = model.enabled RadioButtonSelectionHelper.setupForRadioButtonGroup(model, self, delegateObject: delegateObject) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift index 7d50cf56..239a8d41 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift @@ -6,7 +6,6 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation import MVMCore @@ -17,6 +16,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { public static var identifier: String = "radioButton" public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var state: Bool = false public var enabled: Bool = true @@ -35,6 +35,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case state case enabled case fieldValue @@ -56,9 +57,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { // MARK: - Validation //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { - return fieldValue - } + public func formFieldValue() -> AnyHashable? { fieldValue } //-------------------------------------------------- // MARK: - Codec @@ -76,6 +75,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { } backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) baseValue = state fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) @@ -89,6 +89,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(state, forKey: .state) try container.encode(enabled, forKey: .enabled) diff --git a/MVMCoreUI/BaseClasses/Button.swift b/MVMCoreUI/BaseClasses/Button.swift index 79a72d02..1802e25d 100644 --- a/MVMCoreUI/BaseClasses/Button.swift +++ b/MVMCoreUI/BaseClasses/Button.swift @@ -136,9 +136,11 @@ public typealias ButtonAction = (Button) -> () //-------------------------------------------------- open override func accessibilityActivate() -> Bool { + guard isEnabled else { return false } buttonAction?(self) return buttonAction != nil } + } // MARK: - MVMCoreViewProtocol diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index e2e725c1..a0785c41 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -75,11 +75,12 @@ import MVMCore /// Convenience function. Gets the form validator from the holder and asks it to validate. public static func validate(delegate: FormHolderProtocol?) -> Bool? { - return delegate?.formValidator?.validate() + delegate?.formValidator?.validate() } /// Validates all rule groups. Returns if valid public func validate() -> Bool { + var valid = true guard let formRules = formRules else { return valid } @@ -109,9 +110,10 @@ import MVMCore } } -// mark Form params +// MARK: Form params // TODO: Temporary hacks, rewrite architecture to support this. @objc public extension FormValidator { + @objc func addFormParams(requestParameters: MVMCoreRequestParameters) { let groupName = getGroupName(forPageType: requestParameters.pageType) ?? FormValidator.defaultGroupName let formParams = self.getFormParams(forGroup: groupName) @@ -119,6 +121,7 @@ import MVMCore } @objc func getFormParams( forGroup groupName: String) -> [String: Any] { + var extraParam: [String: Any] = [:] for (fieldKey, field) in fields { if let formFieldValue = field.formFieldValue(), @@ -126,6 +129,7 @@ import MVMCore extraParam[fieldKey] = formFieldValue } } + return extraParam } } @@ -145,4 +149,3 @@ public extension FormValidator { return nil } } - From e5f96dba5c060f5c7d97facbfeaa035dbfb01125 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 10 Dec 2020 14:29:20 -0500 Subject: [PATCH 11/51] considering. --- MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index 4a7a8481..473e033d 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -104,7 +104,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { self.disabledTitleColor = disabledTitleColor } - isAccessibilityElement = isEnabled +// isAccessibilityElement = isEnabled if isEnabled { if let fillColor = buttonModel?.enabledColors.fill { From 197bdd60de50152a7b136d5a25d8e0b98cc5d9c2 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 11 Dec 2020 14:45:50 -0500 Subject: [PATCH 12/51] better test functionality. exposing accessibility for hittable. --- MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift | 2 +- MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index 473e033d..4a7a8481 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -104,7 +104,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { self.disabledTitleColor = disabledTitleColor } -// isAccessibilityElement = isEnabled + isAccessibilityElement = isEnabled if isEnabled { if let fillColor = buttonModel?.enabledColors.fill { diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index b440adaa..0de7f3b4 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -353,6 +353,7 @@ import MVMCore } override open func accessibilityActivate() -> Bool { + guard isEnabled else { return false } sendActions(for: .touchUpInside) return true } From 01908be951c7c5430d2f80767d85a949d03d3f92 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 15 Dec 2020 14:56:18 -0500 Subject: [PATCH 13/51] added script to improve test page access and separate from release compilation. Formatting updates. --- MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift | 3 +++ .../Atoms/FormFields/TextFields/DigitBox.swift | 2 +- .../FormFields/TextFields/DigitEntryField.swift | 4 +--- .../FormFields/TextFields/EntryFieldModel.swift | 4 +--- .../TextFields/ItemDropdownEntryFieldModel.swift | 4 +--- .../Atoms/FormFields/TextFields/MdnEntryField.swift | 9 +++------ .../FormFields/TextFields/TextViewEntryField.swift | 12 ++++++------ .../TextFields/TextViewEntryFieldModel.swift | 4 +--- MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift | 6 ++---- 9 files changed, 19 insertions(+), 29 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index 4a7a8481..8eefe1d7 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -104,7 +104,10 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { self.disabledTitleColor = disabledTitleColor } + #if DEBUG + // Useful to detect with isHittable when performing UI testing. isAccessibilityElement = isEnabled + #endif if isEnabled { if let fillColor = buttonModel?.enabledColors.fill { diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift index 7dce16a4..4d23558f 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift @@ -43,7 +43,7 @@ import UIKit //-------------------------------------------------- public override var showError: Bool { - get { return super.showError } + get { super.showError } set (error) { DispatchQueue.main.async { [weak self] in guard let self = self else { return } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift index b1f202a8..f3cc628e 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift @@ -357,9 +357,7 @@ import UIKit super.set(with: model, delegateObject, additionalData) } - public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 115 - } + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 115 } } // MARK: - TextField Delegate diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift index b7088c96..0cccd0fa 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift @@ -14,9 +14,7 @@ import Foundation // MARK: - Properties //-------------------------------------------------- - public class var identifier: String { - return "" - } + public class var identifier: String { "" } public var backgroundColor: Color? public var accessibilityIdentifier: String? diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift index 0bc1ee0a..10650e20 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift @@ -11,9 +11,7 @@ // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { - return "dropDown" - } + public override class var identifier: String { "dropDown" } public var options: [String] = [] public var selectedIndex: Int? diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift index d80a008b..c1dde689 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift @@ -211,17 +211,14 @@ import MVMCore } @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { - - return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true + proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true } @objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { - - return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true + proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true } @objc public func textFieldShouldClear(_ textField: UITextField) -> Bool { - - return proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true + proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift index e08f3843..54234d16 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift @@ -31,11 +31,11 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele //-------------------------------------------------- public var textViewEntryFieldModel: TextViewEntryFieldModel? { - return model as? TextViewEntryFieldModel + model as? TextViewEntryFieldModel } public override var isEnabled: Bool { - get { return super.isEnabled } + get { super.isEnabled } set (enabled) { super.isEnabled = enabled @@ -53,7 +53,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele } public override var showError: Bool { - get { return super.showError } + get { super.showError } set (error) { if error { @@ -68,7 +68,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele /// The text of this textView. open override var text: String? { - get { return textViewEntryFieldModel?.text } + get { textViewEntryFieldModel?.text } set { textView.text = newValue textViewEntryFieldModel?.text = newValue @@ -77,7 +77,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele /// Placeholder access for the textView. public var placeholder: String? { - get { return textViewEntryFieldModel?.placeholder } + get { textViewEntryFieldModel?.placeholder } set { textView.placeholder = newValue ?? "" textViewEntryFieldModel?.placeholder = newValue @@ -127,7 +127,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele /// If you're using a ViewController, you must set this to it public weak var uiTextViewDelegate: UITextViewDelegate? { - get { return textView.delegate } + get { textView.delegate } set { textView.delegate = newValue } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryFieldModel.swift index ce1173ae..00a07a1c 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryFieldModel.swift @@ -14,9 +14,7 @@ class TextViewEntryFieldModel: TextEntryFieldModel { // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { - return "textView" - } + public override class var identifier: String { "textView" } public var accessibilityText: String? public var fontStyle: Styler.Font = Styler.Font.RegularBodyLarge diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index a22907ce..d04d4999 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -15,7 +15,7 @@ import UIKit //-------------------------------------------------- public var diameter: CGFloat = 30 { - didSet { widthConstraint?.constant = diameter } + didSet { widthConstraint?.constant = diameter } } @objc public override var isSelected: Bool { @@ -34,9 +34,7 @@ import UIKit model as? RadioButtonModel } - lazy public var radioGroupName: String? = { - return radioModel?.fieldKey - }() + lazy public var radioGroupName: String? = { radioModel?.fieldKey }() lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = { From 7dcf1279b707e552aa971dd637f6d11d356cdac3 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 16 Dec 2020 17:08:50 -0500 Subject: [PATCH 14/51] new test flow. improved formatting. --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 3f485099..e41115f7 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -38,7 +38,7 @@ public typealias ActionBlock = () -> () } public var getRange: NSRange { - return NSRange(location: 0, length: text?.count ?? 0) + NSRange(location: 0, length: text?.count ?? 0) } //------------------------------------------------------ @@ -209,7 +209,7 @@ public typealias ActionBlock = () -> () /// Default @objc open class func label() -> Label { - return Label(frame: .zero) + Label(frame: .zero) } //------------------------------------------------------ @@ -794,17 +794,11 @@ extension Label { accessibilityTraits = .staticText } - public func needsToBeConstrained() -> Bool { - return true - } + public func needsToBeConstrained() -> Bool { true } - public func horizontalAlignment() -> UIStackView.Alignment { - return .leading - } + public func horizontalAlignment() -> UIStackView.Alignment { .leading } - public func copyBackgroundColor() -> Bool { - return true - } + public func copyBackgroundColor() -> Bool { true } } // MARK: - Multi-Link Functionality @@ -916,6 +910,7 @@ extension UITapGestureRecognizer { if label.makeWholeViewClickable { return true } + guard let abstractContainer = label.abstractTextContainer() else { return false } let textContainer = abstractContainer.0 let layoutManager = abstractContainer.1 @@ -990,6 +985,7 @@ extension Label { } } } + return false } } From 9baf9e7d0df8105c012f406e842b4f4f1c1b97b3 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Dec 2020 13:03:25 -0500 Subject: [PATCH 15/51] adding accessibility id. formatting. adding test logic. --- MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 2 ++ .../Atomic/Atoms/Selectors/HeartModel.swift | 12 +++++-- .../Atomic/Atoms/Selectors/RadioBox.swift | 32 ++++++++++++------- .../RadioBoxCollectionViewCell.swift | 9 +++++- .../Atoms/Selectors/RadioBoxModel.swift | 20 +++++++++++- .../Atomic/Atoms/Selectors/RadioBoxes.swift | 11 ++++--- .../Atoms/Selectors/RadioBoxesModel.swift | 21 +++++++++++- .../RadioButtonSelectionHelper.swift | 6 +--- .../Atomic/Atoms/Selectors/RadioSwatch.swift | 2 ++ .../RadioSwatchCollectionViewCell.swift | 2 +- .../Atoms/Selectors/RadioSwatchModel.swift | 25 +++++++++++++-- .../Atoms/Selectors/RadioSwatches.swift | 7 ++-- .../Atoms/Selectors/RadioSwatchesModel.swift | 21 +++++++++++- MVMCoreUI/Styles/MFStyler.m | 2 +- 14 files changed, 137 insertions(+), 35 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index 796bf617..09068120 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -71,6 +71,7 @@ import UIKit //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- + open override func setupView() { super.setupView() addTarget(self, action: #selector(tapAction), for: .touchUpInside) @@ -88,6 +89,7 @@ import UIKit self.delegateObject = delegateObject self.additionalData = additionalData guard let model = model as? HeartModel else { return } + accessibilityIdentifier = model.accessibilityIdentifier isSelected = model.isActive } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift index 8001ddd5..815117a9 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift @@ -6,15 +6,15 @@ // 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 accessibilityIdentifier: String? public var isActive: Bool = false public var activeColor: Color = Color(uiColor: .mvmRed) public var inActiveColor: Color = Color(uiColor: .clear) @@ -23,9 +23,11 @@ open class HeartModel: MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case isActive case activeColor case inActiveColor @@ -41,13 +43,18 @@ open class HeartModel: MoleculeModelProtocol { if let isActive = try typeContainer.decodeIfPresent(Bool.self, forKey: .isActive) { self.isActive = isActive } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) + 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 } + if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) { self.action = action } @@ -56,6 +63,7 @@ open class HeartModel: MoleculeModelProtocol { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(isActive, forKey: .isActive) try container.encode(activeColor, forKey: .activeColor) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index d4fa0232..02ba1ea2 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -6,9 +6,12 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation open class RadioBox: Control, MFButtonProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public let label = Label(fontStyle: .RegularBodySmall) public let subTextLabel = Label(fontStyle: .RegularMicro) public var isOutOfStock = false @@ -26,22 +29,20 @@ open class RadioBox: Control, MFButtonProtocol { var additionalData: [AnyHashable: Any]? public var radioBoxModel: RadioBoxModel? { - return model as? RadioBoxModel + model as? RadioBoxModel } public override var isSelected: Bool { - didSet { - updateAccessibility() - } + didSet { updateAccessibility() } } public override var isEnabled: Bool { - didSet { - updateAccessibility() - } + didSet { updateAccessibility() } } + //-------------------------------------------------- // MARK: - MVMCoreViewProtocol + //-------------------------------------------------- open override func updateView(_ size: CGFloat) { super.updateView(size) @@ -75,8 +76,6 @@ open class RadioBox: Control, MFButtonProtocol { isAccessibilityElement = true } - // MARK: - MoleculeViewProtocol - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? RadioBoxModel else { return } @@ -86,6 +85,7 @@ open class RadioBox: Control, MFButtonProtocol { subTextLabel.text = model.subText isOutOfStock = model.strikethrough subTextLabelHeightConstraint?.isActive = (subTextLabel.text?.count ?? 0) == 0 + accessibilityIdentifier = model.accessibilityIdentifier if let color = model.selectedAccentColor?.uiColor { accentColor = color } @@ -99,7 +99,9 @@ open class RadioBox: Control, MFButtonProtocol { accentColor = .mvmRed } + //-------------------------------------------------- // MARK: - State Handling + //-------------------------------------------------- open override func draw(_ layer: CALayer, in ctx: CGContext) { // Draw the strikethrough @@ -213,21 +215,29 @@ open class RadioBox: Control, MFButtonProtocol { return mask } + //-------------------------------------------------- // MARK: - Accessibility + //-------------------------------------------------- + public func updateAccessibility() { + var message = "" + if let labelText = label.text, label.hasText { message += labelText + ", " } + if let subLabelText = subTextLabel.text, subTextLabel.hasText { message += subLabelText + ", " } - accessibilityLabel = message + accessibilityLabel = message accessibilityTraits = .button + if isSelected { accessibilityTraits.insert(.selected) } + if !isEnabled { accessibilityTraits.insert(.notEnabled) } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxCollectionViewCell.swift index 314a2453..d6b9214d 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxCollectionViewCell.swift @@ -6,10 +6,17 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation open class RadioBoxCollectionViewCell: CollectionViewCell { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + public let radioBox = RadioBox() + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func reset() { super.reset() backgroundColor = .clear diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift index 923a65e0..5d5443d0 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift @@ -6,12 +6,16 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers public class RadioBoxModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "radioBox" public var text: String public var subText: String? public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var selectedAccentColor: Color? public var selected: Bool = false public var enabled: Bool = true @@ -19,12 +23,17 @@ import Foundation public var fieldValue: String? public var action: ActionModelProtocol? + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case text case subText case selectedAccentColor case backgroundColor + case accessibilityIdentifier case selected case enabled case strikethrough @@ -32,18 +41,26 @@ import Foundation case action } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) text = try typeContainer.decode(String.self, forKey: .text) subText = try typeContainer.decodeIfPresent(String.self, forKey: .subText) selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) + if let isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { selected = isSelected } + if let isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { enabled = isEnabled } + if let isStrikeTrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { strikethrough = isStrikeTrough } @@ -59,6 +76,7 @@ import Foundation try container.encodeIfPresent(subText, forKey: .subText) try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(selected, forKey: .selected) try container.encode(enabled, forKey: .enabled) try container.encode(strikethrough, forKey: .strikethrough) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift index 0b17061e..c438d6c3 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift @@ -57,11 +57,13 @@ open class RadioBoxes: View { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject - guard let radioBoxesModel = model as? RadioBoxesModel else { return } - boxes = radioBoxesModel.boxes - FormValidator.setupValidation(for: radioBoxesModel, delegate: delegateObject?.formHolderDelegate) + guard let model = model as? RadioBoxesModel else { return } + boxes = model.boxes + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) - backgroundColor = radioBoxesModel.backgroundColor?.uiColor + backgroundColor = model.backgroundColor?.uiColor + accessibilityIdentifier = model.accessibilityIdentifier + registerCells() setHeight() collectionView.reloadData() @@ -168,4 +170,3 @@ extension RadioBoxes: UICollectionViewDelegate { cell.updateAccessibility() } } - diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxesModel.swift index 5b568910..bd1f99d1 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxesModel.swift @@ -6,17 +6,25 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers public class RadioBoxesModel: MoleculeModelProtocol, FormFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "radioBoxes" public var boxes: [RadioBoxModel] public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var selectedAccentColor: Color? public var boxesColor: Color? public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + /// Returns the fieldValue of the selected box, otherwise the text of the selected box. public func formFieldValue() -> AnyHashable? { let selectedBox = boxes.first { (box) -> Bool in @@ -25,20 +33,30 @@ import Foundation return selectedBox?.fieldValue ?? selectedBox?.text } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case selectedAccentColor case backgroundColor + case accessibilityIdentifier case boxesColor case boxes case fieldKey case groupName } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) boxesColor = try typeContainer.decodeIfPresent(Color.self, forKey: .boxesColor) boxes = try typeContainer.decode([RadioBoxModel].self, forKey: .boxes) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) @@ -54,6 +72,7 @@ import Foundation try container.encode(boxes, forKey: .boxes) try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encode(groupName, forKey: .groupName) } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonSelectionHelper.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonSelectionHelper.swift index 4765766b..a5000284 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonSelectionHelper.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonSelectionHelper.swift @@ -6,8 +6,6 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import Foundation - @objcMembers public class RadioButtonSelectionHelper: FormFieldProtocol { //-------------------------------------------------- @@ -77,7 +75,5 @@ import Foundation // MARK: - FormValidationFormFieldProtocol extension RadioButtonSelectionHelper { - public func formFieldValue() -> AnyHashable? { - return selectedRadioButtonModel?.fieldValue - } + public func formFieldValue() -> AnyHashable? { selectedRadioButtonModel?.fieldValue } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index 70f2a459..e661742a 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -62,6 +62,7 @@ open class RadioSwatch: Control, MFButtonProtocol { guard let model = model as? RadioSwatchModel else { return } self.delegateObject = delegateObject self.additionalData = additionalData + accessibilityIdentifier = model.accessibilityIdentifier bottomText.text = model.text isSelected = model.selected isEnabled = model.enabled @@ -77,6 +78,7 @@ open class RadioSwatch: Control, MFButtonProtocol { //------------------------------------------------------ // MARK: - State Handling //------------------------------------------------------ + open override func draw(_ layer: CALayer, in ctx: CGContext) { //Draw the swatch circleLayer?.removeFromSuperlayer() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchCollectionViewCell.swift index 4a9efb4b..96edb174 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchCollectionViewCell.swift @@ -6,7 +6,7 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation + open class RadioSwatchCollectionViewCell: CollectionViewCell { public let radioSwatch = RadioSwatch() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift index 7acb3999..c961e306 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift @@ -6,11 +6,15 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers public class RadioSwatchModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "radioSwatch" public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var color: Color = Color(uiColor: .mvmBlue) public var text: String? public var selected: Bool = false @@ -19,9 +23,14 @@ import Foundation public var fieldValue: String? public var action: ActionModelProtocol? + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case color case text case selected @@ -31,22 +40,33 @@ import Foundation case action } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) { self.color = color } + text = try typeContainer.decodeIfPresent(String.self, forKey: .text) + if let selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { self.selected = selected } + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { self.enabled = enabled } + if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { self.strikethrough = strikethrough } + fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) action = try typeContainer.decodeModelIfPresent(codingKey: .action) } @@ -55,6 +75,7 @@ import Foundation var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(color, forKey: .color) try container.encodeIfPresent(text, forKey: .text) try container.encode(selected, forKey: .selected) @@ -64,5 +85,3 @@ import Foundation try container.encodeModelIfPresent(action, forKey: .action) } } - - diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index 6d4f93d3..036dfaf2 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -55,9 +55,10 @@ open class RadioSwatches: View { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject - guard let radioSwatchesModel = model as? RadioSwatchesModel else { return } - swatches = radioSwatchesModel.swatches - FormValidator.setupValidation(for: radioSwatchesModel, delegate: delegateObject?.formHolderDelegate) + guard let model = model as? RadioSwatchesModel else { return } + swatches = model.swatches + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) + accessibilityIdentifier = model.accessibilityIdentifier collectionView.reloadData() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift index db8e5d5d..60f11a2b 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift @@ -6,16 +6,24 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers public class RadioSwatchesModel: MoleculeModelProtocol, FormFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "radioSwatches" public var backgroundColor: Color? + public var accessibilityIdentifier: String? public var swatches: [RadioSwatchModel] public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + /// Returns the fieldValue of the selected swatch, otherwise the text of selected swatch. public func formFieldValue() -> AnyHashable? { let selectedSwatch = swatches.first { (swatch) -> Bool in @@ -24,17 +32,27 @@ import Foundation return selectedSwatch?.fieldValue ?? selectedSwatch?.text } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case accessibilityIdentifier case swatches case fieldKey case groupName } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .backgaccessibilityIdentifierroundColor) swatches = try typeContainer.decode([RadioSwatchModel].self, forKey: .swatches) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { @@ -47,6 +65,7 @@ import Foundation var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encode(swatches, forKey: .swatches) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encode(groupName, forKey: .groupName) diff --git a/MVMCoreUI/Styles/MFStyler.m b/MVMCoreUI/Styles/MFStyler.m index 130b4c7a..e8d58a26 100644 --- a/MVMCoreUI/Styles/MFStyler.m +++ b/MVMCoreUI/Styles/MFStyler.m @@ -1104,7 +1104,7 @@ CGFloat const LabelWithInternalButtonLineSpace = 2; } + (nonnull NSAttributedString *)styleGetRegularMicroAttributedString:(nullable NSString *)string { - return [MFStyler styleGetRegularBodySmallAttributedString:string genericScaling:YES]; + return [MFStyler styleGetRegularMicroAttributedString:string genericScaling:YES]; } + (nonnull NSAttributedString *)styleGetRegularMicroAttributedString:(nullable NSString *)string genericScaling:(BOOL)genericScaling { From 6b3dd399cfe50c115b213d1c717c2d0309d871a4 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Dec 2020 14:16:55 -0500 Subject: [PATCH 16/51] change func. --- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift index 60f11a2b..d44b631a 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift @@ -52,7 +52,7 @@ required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .backgaccessibilityIdentifierroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) swatches = try typeContainer.decode([RadioSwatchModel].self, forKey: .swatches) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { From af83f4f8b5e2dcceda85db03665ac45ec915c927 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 18 Dec 2020 14:47:11 -0500 Subject: [PATCH 17/51] formatting. json. updates. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 ++-- MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift | 1 + .../TextFields/TextViewEntryField.swift | 3 ++- .../Atoms/{Views => Selectors}/Toggle.swift | 18 ++++++------------ .../{Views => Selectors}/ToggleModel.swift | 10 +++++----- .../Label/LabelAttributeActionModel.swift | 4 +--- .../Views/Label/LabelAttributeColorModel.swift | 4 +--- .../Views/Label/LabelAttributeFontModel.swift | 4 +--- .../Views/Label/LabelAttributeImageModel.swift | 4 +--- .../Views/Label/LabelAttributeModel.swift | 4 +--- .../LabelAttributeStrikeThroughModel.swift | 4 +--- .../Label/LabelAttributeUnderlineModel.swift | 4 +--- 12 files changed, 23 insertions(+), 41 deletions(-) rename MVMCoreUI/Atomic/Atoms/{Views => Selectors}/Toggle.swift (98%) rename MVMCoreUI/Atomic/Atoms/{Views => Selectors}/ToggleModel.swift (93%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 9194a70a..ed3f746f 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -1707,6 +1707,8 @@ D2092348244A51D40044AD09 /* RadioSwatchModel.swift */, AAB9C109243496DD00151545 /* RadioSwatch.swift */, AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */, + D260105223CEA61600764D80 /* ToggleModel.swift */, + 0AA33B392398524F0067DD0F /* Toggle.swift */, AAA7CD68250641F90045B959 /* HeartModel.swift */, AAA7CD6A250642080045B959 /* Heart.swift */, ); @@ -2004,8 +2006,6 @@ D28A838223CCBD3F00DFE4FC /* WheelModel.swift */, 943784F3236B77BB006A1E82 /* Wheel.swift */, 943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */, - D260105223CEA61600764D80 /* ToggleModel.swift */, - 0AA33B392398524F0067DD0F /* Toggle.swift */, 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */, 0AE98BB423FF18D2004C5109 /* Arrow.swift */, 94382085243238D100B43AF3 /* WebViewModel.swift */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift index 5ae68241..633eecc7 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift @@ -142,6 +142,7 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { backgroundColor = color.uiColor } + accessibilityIdentifier = model.accessibilityIdentifier enabledColor = (model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor disabledColor = (model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift index 54234d16..b964ee8c 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/TextViewEntryField.swift @@ -241,6 +241,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele textView.isEditable = model.editable textView.textAlignment = model.textAlignment + textView.accessibilityIdentifier = model.accessibilityIdentifier textView.textColor = model.enabled ? model.enabledTextColor.uiColor : model.disabledTextColor.uiColor textView.font = model.fontStyle.getFont() textView.placeholder = model.placeholder ?? "" @@ -258,7 +259,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele case .email: textView.keyboardType = .emailAddress - default: break + default: break } /// No point in configuring if the TextView is Read-only. diff --git a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift similarity index 98% rename from MVMCoreUI/Atomic/Atoms/Views/Toggle.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index 5712695b..f32ecbbe 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -40,7 +40,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) /// Executes logic before state change. If false, then toggle state will not change and the didToggleAction will not execute. public var shouldToggleAction: ActionBlockConfirmation? = { - return { return true } + return { true } }() // Sizes are from InVision design specs. @@ -69,9 +69,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) /// Simple means to prevent user interaction with the toggle. public var isLocked: Bool = false { - didSet { - isUserInteractionEnabled = !isLocked - } + didSet { isUserInteractionEnabled = !isLocked } } /// The state on the toggle. Default value: false. @@ -109,7 +107,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) } public var toggleModel: ToggleModel? { - return model as? ToggleModel + model as? ToggleModel } //-------------------------------------------------- @@ -414,18 +412,14 @@ public typealias ActionBlockConfirmation = () -> (Bool) } public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return Self.getContainerHeight() + Self.getContainerHeight() } } // MARK: - MVMCoreUIViewConstrainingProtocol extension Toggle { - public func needsToBeConstrained() -> Bool { - return true - } + public func needsToBeConstrained() -> Bool { true } - public func horizontalAlignment() -> UIStackView.Alignment { - return .trailing - } + public func horizontalAlignment() -> UIStackView.Alignment { .trailing } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift similarity index 93% rename from MVMCoreUI/Atomic/Atoms/Views/ToggleModel.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index b39003d2..4990df73 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ToggleModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift @@ -6,8 +6,6 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import UIKit - public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol { //-------------------------------------------------- @@ -15,6 +13,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo //-------------------------------------------------- public static var identifier: String = "toggle" + public var accessibilityIdentifier: String? public var backgroundColor: Color? public var state: Bool = false public var animated: Bool = true @@ -42,6 +41,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo case enabled case action case backgroundColor + case accessibilityIdentifier case alternateAction case accessibilityText case onTintColor @@ -56,9 +56,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo // MARK: - Methods //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { - return state - } + public func formFieldValue() -> AnyHashable? { state } //-------------------------------------------------- // MARK: - Initializer @@ -91,6 +89,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo action = try typeContainer.decodeModelIfPresent(codingKey: .action) alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) if let onTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onTintColor) { self.onTintColor = onTintColor @@ -120,6 +119,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) try container.encode(moleculeName, forKey: .moleculeName) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift index f9920dcc..1ecbe874 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift @@ -12,9 +12,7 @@ open class LabelAttributeActionModel: LabelAttributeModel { // MARK: - Properties //-------------------------------------------------- - override public class var identifier: String { - return "action" - } + override public class var identifier: String { "action" } var action: ActionModelProtocol diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift index b86251c3..94579d02 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift @@ -12,9 +12,7 @@ // MARK: - Properties //-------------------------------------------------- - override public class var identifier: String { - return "color" - } + override public class var identifier: String { "color" } var textColor: Color? diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift index 59dbac3d..72d57e40 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift @@ -12,9 +12,7 @@ // MARK: - Properties //-------------------------------------------------- - override public class var identifier: String { - return "font" - } + override public class var identifier: String { "font" } var style: Styler.Font? var name: String? diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift index 70cd90fd..12e8d68d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift @@ -12,9 +12,7 @@ class LabelAttributeImageModel: LabelAttributeModel { // MARK: - Properties //-------------------------------------------------- - override public class var identifier: String { - return "image" - } + override public class var identifier: String { "image" } var size: CGFloat? var name: String? diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift index 4c456a67..7df4097a 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift @@ -12,9 +12,7 @@ // MARK: - Properties //-------------------------------------------------- - public static var categoryName: String { - "\(LabelAttributeModel.self)" - } + public static var categoryName: String { "\(LabelAttributeModel.self)" } public static var categoryCodingKey: String { "type" } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift index e84c1804..83744a68 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift @@ -12,9 +12,7 @@ // MARK: - Properties //-------------------------------------------------- - override public class var identifier: String { - return "strikethrough" - } + override public class var identifier: String { "strikethrough" } //-------------------------------------------------- // MARK: - Initializer diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift index a3adbe00..f3578418 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift @@ -14,9 +14,7 @@ import UIKit // MARK: - Properties //-------------------------------------------------- - override public class var identifier: String { - return "underline" - } + override public class var identifier: String { "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. From c963c05f60eebc2021fa2fc97d48740c195ac0d7 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 20 Jan 2021 09:42:09 -0500 Subject: [PATCH 18/51] optional --- .../HorizontalCombinationViews/TabBarModel.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift index 9de2cd54..581b890e 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift @@ -60,7 +60,7 @@ public class TabBarModel: MoleculeModelProtocol { } public class TabBarItemModel: Codable { - var title: String + var title: String? var image: String var action: ActionModelProtocol @@ -70,7 +70,7 @@ public class TabBarItemModel: Codable { case action } - public init(with title: String, image: String, action: ActionModelProtocol) { + public init(with title: String?, image: String, action: ActionModelProtocol) { self.title = title self.image = image self.action = action @@ -78,14 +78,14 @@ public class TabBarItemModel: Codable { required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - title = try typeContainer.decode(String.self, forKey: .title) + title = try typeContainer.decodeIfPresent(String.self, forKey: .title) image = try typeContainer.decode(String.self, forKey: .image) action = try typeContainer.decodeModel(codingKey: .action) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(title, forKey: .title) + try container.encodeIfPresent(title, forKey: .title) try container.encode(image, forKey: .image) try container.encodeModel(action, forKey: .action) } From 4038adc556932d6616692f153ad3b8008e83403c Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 22 Jan 2021 17:09:01 -0500 Subject: [PATCH 19/51] actions action and minor cleanup --- .../TextFields/BaseDropdownEntryField.swift | 4 +- .../Atomic/Atoms/Selectors/Checkbox.swift | 8 +-- .../Atomic/Atoms/Views/Label/Label.swift | 4 +- MVMCoreUI/Atomic/Atoms/Views/Toggle.swift | 14 ++-- .../Templates/ModalMoleculeListTemplate.swift | 12 ++-- .../ModalMoleculeStackTemplate.swift | 13 ++-- .../Templates/ModalSectionListTemplate.swift | 9 +-- .../OtherHandlers/MVMCoreUIActionHandler.h | 14 ---- .../OtherHandlers/MVMCoreUIActionHandler.m | 66 ------------------- 9 files changed, 22 insertions(+), 122 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index 82e46306..b89a56e3 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -92,9 +92,9 @@ import UIKit } func performDropdownAction() { - if let baseDropdownEntryFieldModel = baseDropdownEntryFieldModel, let actionModel = baseDropdownEntryFieldModel.action, let actionMap = actionModel.toJSON() { + if let baseDropdownEntryFieldModel = baseDropdownEntryFieldModel, let actionModel = baseDropdownEntryFieldModel.action { let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: baseDropdownEntryFieldModel) - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataWithSource, delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index edc37628..c85b3274 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -396,11 +396,9 @@ import MVMCore } private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - if let actionMap = actionModel.toJSON() { - var additionalDatatoUpdate = additionalData ?? [:] - additionalDatatoUpdate[KeySourceModel] = checkboxModel - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) - } + var additionalDatatoUpdate = additionalData ?? [:] + additionalDatatoUpdate[KeySourceModel] = checkboxModel + MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 3f485099..bb1745a3 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -381,9 +381,7 @@ public typealias ActionBlock = () -> () } case let actionAtt as LabelAttributeActionModel: addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) { - if let data = try? actionAtt.action.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } + MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionAtt.action, additionalData: additionalData, delegateObject: delegateObject) } addActionAttributes(range: range, string: attributedString) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift index 5712695b..931b272a 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift @@ -392,21 +392,17 @@ public typealias ActionBlockConfirmation = () -> (Bool) accessibilityLabel = accessibileString } - let actionMap = model.action?.toJSON() - let alternateActionMap = model.alternateAction?.toJSON() let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) - if actionMap != nil || alternateActionMap != nil { + if model.action != nil || model.alternateAction != nil { didToggleAction = { [weak self] in guard let self = self else { return } if self.isOn { - if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) + if let action = model.action { + MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) } } else { - if alternateActionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) - } else if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) + if let action = model.alternateAction ?? model.action { + MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift index ac22cd18..1a82333d 100644 --- a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift @@ -29,14 +29,10 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate { super.handleNewData() closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { [weak self] _ in - guard let self = self else { - return - } - guard let model = self.templateModel as? ModalListPageTemplateModel, 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) + guard let self = self else { return } + let closeAction = (self.templateModel as? ModalListPageTemplateModel)?.closeAction ?? + ActionBackModel() + MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar) }) } diff --git a/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift index 78a1e157..1db1bb9c 100644 --- a/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift @@ -18,15 +18,10 @@ open class ModalMoleculeStackTemplate: MoleculeStackTemplate { 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? ModalStackPageTemplateModel, 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) + guard let self = self else { return } + let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ?? + ActionBackModel() + MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar) }) } } diff --git a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift index 77ee705c..424fc6c5 100644 --- a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift @@ -19,12 +19,9 @@ open class ModalSectionListTemplate: SectionListTemplate { 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) + let closeAction = (self.templateModel as? ModalSectionListTemplateModel)?.closeAction ?? + ActionBackModel() + MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar) }) } } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.h b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.h index f6b89806..c1dee62a 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.h @@ -27,20 +27,6 @@ NS_ASSUME_NONNULL_BEGIN // Shows a topnotification new molecular - (void)topNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -#pragma mark - Deprecated - -// Shows a popup -- (void)popupAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Shows a top alert -- (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Shows a molecular top alert -- (void)topNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Collapses the current top notification -- (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - @end NS_ASSUME_NONNULL_END diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.m b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.m index 79f92990..e2adbd74 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.m @@ -122,70 +122,4 @@ } } -#pragma mark - Deprecated - -- (BOOL)handleOtherActions:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - if ([actionType isEqualToString:KeyActionTypePopup]) { - [self popupAction:actionInformation additionalData:additionalData delegate:delegate]; - return YES; - } else if ([actionType isEqualToString:KeyActionTypeTopAlert]) { - [self topAlertAction:actionInformation additionalData:additionalData delegate:delegate]; - return YES; - } else if ([actionType isEqualToString:KeyActionTypeCollapseNotification]) { - [self collapseNotificationAction:actionInformation additionalData:additionalData delegate:delegate]; - return YES; - } else if ([actionType isEqualToString:KeyActionTypeTopNotification]) { - [self topNotificationAction:actionInformation additionalData:additionalData delegate:delegate]; - return YES; - } - return NO; -} - -- (void)popupAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Perform a popup. - NSString *pageTypeForPopup = [actionInformation stringForKey:KeyPageType]; - [[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForPopup queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) { - - MVMCoreErrorObject *error = nil; - MVMCoreAlertObject *alertObject = [MVMCoreAlertObject alertObjectWithPage:jsonDictionary isGreedy:NO additionalData:additionalData delegate:delegate error:&error]; - if ([delegate respondsToSelector:@selector(willShowPopupWithAlertObject:alertJson:)]) { - [((id )delegate) willShowPopupWithAlertObject:alertObject alertJson:jsonDictionary]; - } - - if (alertObject) { - [[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:alertObject]; - } else { - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - } - }]; -} - -- (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Perform a top alert. - NSString *pageTypeForTopAlert = [actionInformation stringForKey:KeyPageType]; - [[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForTopAlert queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) { - - NSDictionary *responseInfo = [jsonDictionary dict:KeyResponseInfo]; - if (responseInfo) { - MVMCoreAlertObject *alertObject = [MVMCoreAlertObject alertObjectForPageType:pageTypeForTopAlert responseInfo:responseInfo additionalData:additionalData actionDelegate:delegate]; - if ([delegate respondsToSelector:@selector(willShowTopAlertWithAlertObject:alertJson:)]) { - alertObject = [((id )delegate) willShowTopAlertWithAlertObject:alertObject alertJson:jsonDictionary]; - } - [alertObject showAlert]; - } - }]; -} - -- (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Collapse the current notification. - if ([[CoreUIObject sharedInstance].globalTopAlertDelegate respondsToSelector:@selector(getTopAlertView)]) { - [[[CoreUIObject sharedInstance].globalTopAlertDelegate getTopAlertView] collapseNotification]; - } -} - -- (void)topNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - //Handle molecular topnotification - [[MVMCoreUITopAlertView sharedGlobal] showTopAlertWith:[actionInformation dict:@"topNotification"] ?: @{}]; -} - @end From d63a50427222e11006b2d2d7334f7e7f952b8358 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 25 Jan 2021 09:26:39 -0500 Subject: [PATCH 20/51] typo fix --- MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index c85b3274..b80fe9ff 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -396,9 +396,9 @@ import MVMCore } private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - var additionalDatatoUpdate = additionalData ?? [:] - additionalDatatoUpdate[KeySourceModel] = checkboxModel - MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) + var additionalDataToUpdate = additionalData ?? [:] + additionalDataToUpdate[KeySourceModel] = checkboxModel + MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataToUpdate, delegateObject: delegateObject) } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { From be426c756fb10c5e41d03127623a1ee6c2de0c97 Mon Sep 17 00:00:00 2001 From: Subhankar Date: Wed, 27 Jan 2021 22:30:06 +0530 Subject: [PATCH 21/51] scope modification for product detail molecule --- .../Molecules/VerticalCombinationViews/HeadlineBody.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift index d111d1c5..ba95a5d3 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -54,7 +54,7 @@ open class HeadlineBody: View { } } - func styleLandingPageHeader() { + public func styleLandingPageHeader() { headlineLabel.setFontStyle(.Title2XLarge) messageLabel.setFontStyle(.RegularBodySmall) spaceBetweenLabelsConstant = PaddingTwo From a4c7c63ef30b25dfef10ce49fd51c23013679db3 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 28 Jan 2021 15:47:15 -0500 Subject: [PATCH 22/51] video molecule BGVideoImageMolecule --- MVMCoreUI.xcodeproj/project.pbxproj | 24 +++ MVMCoreUI/Atomic/Atoms/Views/Video.swift | 162 ++++++++++++++++++ .../Atomic/Atoms/Views/VideoDataManager.swift | 120 +++++++++++++ MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 58 +++++++ .../Atoms/Views/VideoPauseBehavior.swift | 110 ++++++++++++ .../BGVideoImageMolecule.swift | 98 +++++++++++ .../BGVideoImageMoleculeModel.swift | 38 ++++ .../ThreeLayerFillMiddleTemplate.swift | 8 + .../ScrollingViewController.swift | 6 + MVMCoreUI/Behaviors/PageBehavior.swift | 7 +- 10 files changed, 630 insertions(+), 1 deletion(-) create mode 100644 MVMCoreUI/Atomic/Atoms/Views/Video.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift create mode 100644 MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMolecule.swift create mode 100644 MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMoleculeModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 2dc86525..a1a735a7 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -402,6 +402,12 @@ D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA74C248589C800B75CB8 /* TabPageModelProtocol.swift */; }; D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */; }; + D29C558A25C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C558925C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift */; }; + D29C558D25C05C990082E7D6 /* BGVideoImageMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C558C25C05C990082E7D6 /* BGVideoImageMolecule.swift */; }; + D29C559025C095210082E7D6 /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C558F25C095210082E7D6 /* Video.swift */; }; + D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559225C0992D0082E7D6 /* VideoModel.swift */; }; + D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559525C099630082E7D6 /* VideoDataManager.swift */; }; + D29C559C25C20D6D0082E7D6 /* VideoPauseBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559B25C20D6D0082E7D6 /* VideoPauseBehavior.swift */; }; D29C94D5242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */; }; D29DF0D121E404D4003B2FB9 /* MVMCoreUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */; }; @@ -944,6 +950,12 @@ D28BA74C248589C800B75CB8 /* TabPageModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPageModelProtocol.swift; sourceTree = ""; }; D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewConstrainingProtocol.h; sourceTree = ""; }; D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleMolecule.swift; sourceTree = ""; }; + D29C558925C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGVideoImageMoleculeModel.swift; sourceTree = ""; }; + D29C558C25C05C990082E7D6 /* BGVideoImageMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGVideoImageMolecule.swift; sourceTree = ""; }; + D29C558F25C095210082E7D6 /* Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Video.swift; sourceTree = ""; }; + D29C559225C0992D0082E7D6 /* VideoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoModel.swift; sourceTree = ""; }; + D29C559525C099630082E7D6 /* VideoDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDataManager.swift; sourceTree = ""; }; + D29C559B25C20D6D0082E7D6 /* VideoPauseBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPauseBehavior.swift; sourceTree = ""; }; D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUICommonViewsUtility+Extension.swift"; sourceTree = ""; }; D29DF0CC21E404D4003B2FB9 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUI.h; sourceTree = ""; }; @@ -1676,6 +1688,8 @@ D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */, D253BB9B245874F8002DE544 /* BGImageMolecule.swift */, + D29C558925C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift */, + D29C558C25C05C990082E7D6 /* BGVideoImageMolecule.swift */, ); path = OtherContainers; sourceTree = ""; @@ -2020,6 +2034,10 @@ AA37CBD42519072F0027344C /* Stars.swift */, AA07EA902510A442009A2AE3 /* StarModel.swift */, AA07EA922510A451009A2AE3 /* Star.swift */, + D29C559525C099630082E7D6 /* VideoDataManager.swift */, + D29C559B25C20D6D0082E7D6 /* VideoPauseBehavior.swift */, + D29C559225C0992D0082E7D6 /* VideoModel.swift */, + D29C558F25C095210082E7D6 /* Video.swift */, ); path = Views; sourceTree = ""; @@ -2651,6 +2669,7 @@ 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, BB54C5212434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift in Sources */, D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */, + D29C559C25C20D6D0082E7D6 /* VideoPauseBehavior.swift in Sources */, 0A775F2824893937009EFB58 /* ThreeHeadlineBodyLinkModel.swift in Sources */, 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */, D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */, @@ -2742,6 +2761,7 @@ AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */, + D29C558A25C05C7D0082E7D6 /* BGVideoImageMoleculeModel.swift in Sources */, 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */, BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */, AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */, @@ -2781,6 +2801,7 @@ D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, AAB7EDF1246ADA2A00E54929 /* ListProgressBarThin.swift in Sources */, 8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */, + D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */, D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */, @@ -2846,6 +2867,7 @@ D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */, C003506123AA94CD00B6AC29 /* Button.swift in Sources */, DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */, + D29C559025C095210082E7D6 /* Video.swift in Sources */, D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */, AA104B1C24474A76004D2810 /* HeadersH2ButtonsModel.swift in Sources */, 0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */, @@ -2871,6 +2893,7 @@ BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */, C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */, + D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */, 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */, D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, @@ -2888,6 +2911,7 @@ AA633B3324989ED500731E80 /* HeadersH2PricingTwoRows.swift in Sources */, 01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */, 0A6682A22434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift in Sources */, + D29C558D25C05C990082E7D6 /* BGVideoImageMolecule.swift in Sources */, EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */, 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */, 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/Video.swift b/MVMCoreUI/Atomic/Atoms/Views/Video.swift new file mode 100644 index 00000000..26904344 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/Video.swift @@ -0,0 +1,162 @@ +// +// Video.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/26/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// +import AVKit + +open class Video: View { + public let videoViewController = AVPlayerViewController() + + // TODO: reuse. + private let videoQueue = DispatchQueue(label: "serial.video.queue") + + /// Used to track the state and respond.. + private var stateKVOToken: NSKeyValueObservation? + + private weak var pauseBehavior: BlockPageVisibilityBehavior? + + private weak var scrollBehavior: BlockPageScrolledBehavior? + + private var visible = true + + open override func setupView() { + super.setupView() + videoViewController.view.translatesAutoresizingMaskIntoConstraints = false + addSubview(videoViewController.view) + NSLayoutConstraint.constraintPinSubview(toSuperview: videoViewController.view) + videoViewController.videoGravity = .resizeAspectFill + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? VideoModel else { return } + videoViewController.showsPlaybackControls = model.showControls + videoViewController.player = model.videoDataManager.player + addStateObserver() + switch (model.videoDataManager.videoState) { + case .none: + // Begin loading the video + model.videoDataManager.loadVideo() + case .loaded: + // Video loaded + if model.alwaysReset { + // Always start video at the beginning. + model.videoDataManager.player?.seek(to: .zero) + } + if model.autoPlay { + model.videoDataManager.player?.play() + } + default: + break + } + + // Handle pause behavior + addVisibleBehavoir(to: model, delegateObject: delegateObject) + addScrolledBehavoir(to: model, delegateObject: delegateObject) + } + + /// Listens and responds to video loading state changes. + private func addStateObserver() { + removeStateObserver() + + guard stateKVOToken == nil, + let model = model as? VideoModel else { return } + + // To know when the video player item is done loading. + stateKVOToken = + model.videoDataManager.observe(\.videoState) { [weak self] (item, change) in + guard let self = self, + let model = self.model as? VideoModel, + item == model.videoDataManager else { return } + + switch item.videoState { + case .loaded: + // Setting videoController's player must be in the main thread + MVMCoreDispatchUtility.performSyncBlock(onMainThread: { + // Play the video + self.videoViewController.player = item.player + if model.autoPlay { + item.player?.play() + } + UIAccessibility.post(notification: .screenChanged, argument: self) + }) + case .failed: + if let errorObject = item.loadFailedError { + MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject) + } + default: + break + } + } + } + + private func removeStateObserver() { + stateKVOToken?.invalidate() + stateKVOToken = nil + } + + deinit { + removeStateObserver() + } + + /// Adds a behavoir to pause the video on page hidden behavoir and unpause if necessary on page shown. + open func addVisibleBehavoir(to model: VideoModel, delegateObject: MVMCoreUIDelegateObject?) { + let onShow = { [weak self] in + guard self?.visible == true, + let model = self?.model as? VideoModel, + model.autoPlay else { return } + model.videoDataManager.player?.play() + } + let onHide: () -> Void = { [weak self] in + guard self?.visible == true, + let model = self?.model as? VideoModel else { return } + model.videoDataManager.player?.pause() + } + + if pauseBehavior != nil { + pauseBehavior?.pageShownHandler = onShow + pauseBehavior?.pageHiddenHandler = onHide + } else { + guard let delegate = delegateObject?.moleculeDelegate, + let page = delegate as? PageProtocol, + var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } + var behavoirs = pageModel.behaviors ?? [] + let pauseBehavior = BlockPageVisibilityBehavior(with: onShow, onPageHiddenHandler: onHide) + behavoirs.append(pauseBehavior) + pageModel.behaviors = behavoirs + self.pauseBehavior = pauseBehavior + } + } + + open func addScrolledBehavoir(to model: VideoModel, delegateObject: MVMCoreUIDelegateObject?) { + + let onScroll = { [weak self] (scrollView: UIScrollView) in + // If visible to not visible, pause video. + // If not visible to visible, unpause if needed, add visible behavior + // visible == +// if !visible { +// pause +// } else { +// let model = self?.model as? VideoModel, +// model.autoPlay else { return } +// model.videoDataManager.player?.play() +// } + } + + if scrollBehavior != nil { + scrollBehavior?.pageScrolledHandler = onScroll + } else { + guard let delegate = delegateObject?.moleculeDelegate, + let page = delegate as? PageProtocol, + var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } + var behavoirs = pageModel.behaviors ?? [] + let scrollBehavior = BlockPageScrolledBehavior(with: onScroll) + behavoirs.append(scrollBehavior) + pageModel.behaviors = behavoirs + self.scrollBehavior = scrollBehavior + } + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift new file mode 100644 index 00000000..ab6d74e4 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift @@ -0,0 +1,120 @@ +// +// VideoDataManager.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/26/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class VideoDataManager: NSObject { + + @objc public enum State: Int { + case none + case loading + case loaded + case failed + } + + public let videoURLString: String + public var player: AVPlayer? + + // Thread Safe video state handling. + private var _videoState = State.none + private let videoStatueQueue = DispatchQueue(label: "concurrent.video.state.queue", attributes: .concurrent) + + /// The state of the video. Use KVO to listen for state changes. + @objc public var videoState: State { + get { + var state = State.none + videoStatueQueue.sync { + state = _videoState + } + return state + } + set { + willChangeValue(for: \.videoState) + videoStatueQueue.async(flags: .barrier) { + self._videoState = newValue + } + didChangeValue(for: \.videoState) + } + } + + /// Set when the state is set to failed. Follows the same pattern as apple's AVPlayerItem + public var loadFailedError: MVMCoreErrorObject? + + private var kvoToken: NSKeyValueObservation? + + public init(with videoURLString: String) { + self.videoURLString = videoURLString + } + + public func loadVideo() { + guard videoState != .loading else { return } + removeVideoObserver() + player = nil + videoState = .loading + + //Asset loading needs time, calling async method. by tracking asset's propety "duration", if we get the value of duration, we can treat asset load successfully. + let tracksKey = "duration" + MVMCoreCache.shared()?.playerAsset(fromFileName: videoURLString, trackKeys: [tracksKey], onComplete: { [weak self] (asset, fileName, errorObject) in + guard let asset = asset else { + self?.loadFailedError = errorObject + self?.videoState = .failed + return + } + + var error: NSError? = nil + let tracksStatus = asset.statusOfValue(forKey: tracksKey, error: &error) + switch tracksStatus { + case .loaded: + //When Assets load successfully, we create playerItem and add playerItem into AVPlayer + self?.player = AVPlayer(playerItem: AVPlayerItem(asset: asset)) + self?.addObserverToPlayerItem() + case .failed: + //Asset load fail + //Since checking asset status here, no need to check player.currenItem.asset's media tracks when play button is clicked anymore. + if let error = error, + let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: (asset as? AVURLAsset)?.url.absoluteString ?? self?.videoURLString) { + self?.loadFailedError = errorObject + } + self?.videoState = .failed + default: + break + } + }) + } + + private func addObserverToPlayerItem() { + removeVideoObserver() + + // To know when the video player item is done loading. + guard kvoToken == nil else { return } + kvoToken = player?.currentItem?.observe(\.status) { [weak self] (item, change) in + guard item == self?.player?.currentItem else { return } + switch item.status { + case .readyToPlay: + self?.videoState = .loaded + case .failed: + if let error = item.error, + let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: (item.asset as? AVURLAsset)?.url.absoluteString ?? self?.videoURLString) { + self?.loadFailedError = errorObject + } + self?.videoState = .failed + default: + break + } + } + } + + private func removeVideoObserver() { + kvoToken?.invalidate() + kvoToken = nil + } + + deinit { + removeVideoObserver() + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift new file mode 100644 index 00000000..d335f375 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -0,0 +1,58 @@ +// +// VideoModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/26/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class VideoModel: MoleculeModelProtocol { + public static var identifier = "video" + public var backgroundColor: Color? + public var video: String + public var showControls = false + public var autoPlay = true + public var alwaysReset = false + + /// Keeps a reference to the video data. + public var videoDataManager: VideoDataManager + + private enum CodingKeys: String, CodingKey { + case moleculeName + case video + case showControls + case autoPlay + case alwaysReset + } + + public init(_ video: String) { + self.video = video + videoDataManager = VideoDataManager(with: video) + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + video = try typeContainer.decode(String.self, forKey:.video) + videoDataManager = VideoDataManager(with: video) + if let showControls = try typeContainer.decodeIfPresent(Bool.self, forKey: .showControls) { + self.showControls = showControls + } + if let autoPlay = try typeContainer.decodeIfPresent(Bool.self, forKey: .autoPlay) { + self.autoPlay = autoPlay + } + if let alwaysReset = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysReset) { + self.alwaysReset = alwaysReset + } + } + + open func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(video, forKey: .video) + try container.encode(showControls, forKey: .showControls) + try container.encode(autoPlay, forKey: .autoPlay) + try container.encode(alwaysReset, forKey: .alwaysReset) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift new file mode 100644 index 00000000..73293a8b --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift @@ -0,0 +1,110 @@ +// +// VideoPauseBehavior.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/27/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class BlockPageVisibilityBehavior: PageVisibilityBehavior { + + public static var identifier = "blockPageVisibilityBehavior" + + public var pageShownHandler: () -> Void + public var pageHiddenHandler: () -> Void + + public init(with onPageShownHandler: @escaping () -> Void, onPageHiddenHandler: @escaping () -> Void) { + self.pageShownHandler = onPageShownHandler + self.pageHiddenHandler = onPageHiddenHandler + } + + private enum CodingKeys: String, CodingKey { + case behaviorName + } + + // This class is not meant to be decoded and encoded really. + public required init(from decoder: Decoder) throws { + pageShownHandler = {} + pageHiddenHandler = {} + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(behaviorName, forKey: .behaviorName) + } + + //MARK:- PageVisibilityBehavior + public func onPageShown() { + pageShownHandler() + } + + public func onPageHidden() { + pageHiddenHandler() + } +} + +public class BlockPageScrolledBehavior: PageScrolledBehavior { + + public static var identifier = "blockPageScrolledBehavior" + + public var pageScrolledHandler: (_ scrollView: UIScrollView) -> Void + + public init(with onPageScrolledHandler: @escaping (_ scrollView: UIScrollView) -> Void) { + self.pageScrolledHandler = onPageScrolledHandler + } + + private enum CodingKeys: String, CodingKey { + case behaviorName + } + + // This class is not meant to be decoded and encoded really. + public required init(from decoder: Decoder) throws { + pageScrolledHandler = { (scrollView) in } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(behaviorName, forKey: .behaviorName) + } + + public func pageScrolled(scrollView: UIScrollView) { + pageScrolledHandler(scrollView) + } +} + + +public class VideoPauseBehavior: PageVisibilityBehavior { + + public static var identifier = "videoPause" + + public weak var videoModel: VideoModel? + public init(with videoModel: VideoModel) { + self.videoModel = videoModel + } + + private enum CodingKeys: String, CodingKey { + case behaviorName + } + + //MARK:- PageVisibilityBehavior + + public func onPageShown() { + // TODO: Only do this if attached to a visible view... + if videoModel?.autoPlay == true { + videoModel?.videoDataManager.player?.play() + } + } + + public func onPageHidden() { + videoModel?.videoDataManager.player?.pause() + } + + public required init(from decoder: Decoder) throws {} + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(behaviorName, forKey: .behaviorName) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMolecule.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMolecule.swift new file mode 100644 index 00000000..f57298aa --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMolecule.swift @@ -0,0 +1,98 @@ +// +// BGVideoImageMolecule.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/26/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class BGVideoImageMolecule: BGImageMolecule { + + public let video = Video() + + /// Used to hide video after loaded. + private var stateKVOToken: NSKeyValueObservation? + private var endObserver: NSObjectProtocol? + + open override func setupView() { + super.setupView() + insertSubview(video, aboveSubview: image) + NSLayoutConstraint.constraintPinSubview(toSuperview: video) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? BGVideoImageMoleculeModel else { return } + video.set(with: model.video, delegateObject, additionalData) + video.isHidden = shouldVideoBeHidden() + addStateObserver() + } + + open func shouldVideoBeHidden() -> Bool { + guard let model = model as? BGVideoImageMoleculeModel, + model.video.videoDataManager.videoState != .failed else { + return true + } + guard model.video.videoDataManager.videoState == .loaded, + let player = model.video.videoDataManager.player, + let item = player.currentItem, + item.currentTime() == item.duration else { + return false + } + return true + } + + /// Listens and responds to video loading state changes to add the end observer. + private func addStateObserver() { + removeStateObserver() + + guard stateKVOToken == nil, + let model = model as? BGVideoImageMoleculeModel else { return } + + // To know when the video player item is done loading. + stateKVOToken = + model.video.videoDataManager.observe(\.videoState) { [weak self] (item, change) in + guard let self = self, + let model = self.model as? BGVideoImageMoleculeModel, + item == model.video.videoDataManager else { return } + + switch item.videoState { + case .loaded: + self.addEndObserver() + case .failed: + MVMCoreDispatchUtility.performBlock(onMainThread: { + self.video.isHidden = true + }) + default: + break + } + } + } + + private func removeStateObserver() { + stateKVOToken?.invalidate() + stateKVOToken = nil + } + + private func addEndObserver() { + removeStateObserver() + guard let model = model as? BGVideoImageMoleculeModel, + let item = model.video.videoDataManager.player?.currentItem else { return } + endObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item, queue: OperationQueue.main) { [weak self] (notification) in + self?.video.isHidden = true + } + } + + private func removeEndObserver() { + guard let endObserver = endObserver else { return } + NotificationCenter.default.removeObserver(endObserver) + self.endObserver = nil + } + + deinit { + removeStateObserver() + removeEndObserver() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMoleculeModel.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMoleculeModel.swift new file mode 100644 index 00000000..f2ee20af --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/BGVideoImageMoleculeModel.swift @@ -0,0 +1,38 @@ +// +// BGVideoImageMoleculeModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/26/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class BGVideoImageMoleculeModel: BGImageMoleculeModel { + open override class var identifier: String { + return "bgVideoImageContainer" + } + + public var video: VideoModel + + private enum CodingKeys: String, CodingKey { + case video + } + + public init(_ video: VideoModel, image: ImageViewModel, molecule: MoleculeModelProtocol) { + self.video = video + super.init(image, molecule: molecule) + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + video = try typeContainer.decode(VideoModel.self, forKey:.video) + 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(video, forKey: .video) + } +} diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplate.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplate.swift index 50e55a45..81c3b6a8 100644 --- a/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplate.swift @@ -14,6 +14,14 @@ import Foundation return 0 } + open override func loadView() { + super.loadView() + // The height is used to keep the bottom view at the bottom. + if let contentView = contentView, let scrollView = scrollView { + contentView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.safeAreaLayoutGuide.heightAnchor).isActive = true + } + } + open override func handleNewData() { super.handleNewData() heightConstraint?.isActive = true diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift index fcbcf878..9a9a42e3 100644 --- a/MVMCoreUI/BaseControllers/ScrollingViewController.swift +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -73,6 +73,12 @@ open class ScrollingViewController: ViewController { scrollView.flashScrollIndicators() } + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + executeBehaviors { (behavior: PageScrolledBehavior) in + behavior.pageScrolled(scrollView: scrollView) + } + } + //-------------------------------------------------- // MARK: - Keyboard Handling //-------------------------------------------------- diff --git a/MVMCoreUI/Behaviors/PageBehavior.swift b/MVMCoreUI/Behaviors/PageBehavior.swift index d8fd99a3..ed17daff 100644 --- a/MVMCoreUI/Behaviors/PageBehavior.swift +++ b/MVMCoreUI/Behaviors/PageBehavior.swift @@ -37,8 +37,13 @@ public protocol PageVisibilityBehavior: PageBehaviorProtocol { } +public protocol PageScrolledBehavior: PageBehaviorProtocol { + + func pageScrolled(scrollView: UIScrollView) +} + public protocol PageBehaviorsTemplateProtocol { - var behaviors: [PageBehaviorProtocol]? { get } + var behaviors: [PageBehaviorProtocol]? { get set } } From a7a7ca9604a60b4663741192bac1faf4deafc3b0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 29 Jan 2021 10:18:20 -0500 Subject: [PATCH 23/51] optional modules --- 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 f0ed1ba4..abaa4130 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -74,7 +74,7 @@ import UIKit } open func modulesToListenFor() -> [String]? { - loadObject?.requestParameters?.modules as? [String] + loadObject?.requestParameters?.allModules() as? [String] } @objc open func responseJSONUpdated(notification: Notification) { From 7b4f7885067e2c82a1dc5636d9b5e674fd20841e Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 29 Jan 2021 16:31:30 -0500 Subject: [PATCH 24/51] Bug fix and warning fix --- .../Molecules/HorizontalCombinationViews/TabBar.swift | 7 +++++-- MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m | 2 +- MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift index 1ee7ee71..1565cf30 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift @@ -57,8 +57,11 @@ import Foundation var tabs: [UITabBarItem] = [] for (index, tab) in model.tabs.enumerated() { let tabBarItem = UITabBarItem(title: tab.title, image: MVMCoreCache.shared()?.getImageFromRegisteredBundles(tab.image), tag: index) - tabBarItem.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -3) - tabBarItem.setTitleTextAttributes([NSAttributedString.Key.font: MFFonts.mfFontTXRegular(8)], for: .normal) + if #available(iOS 13.0, *) { + } else { + tabBarItem.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -3) + tabBarItem.setTitleTextAttributes([NSAttributedString.Key.font: MFFonts.mfFontTXRegular(8)], for: .normal) + } tabs.append(tabBarItem) } setItems(tabs, animated: false) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m index 55468c95..46ecd793 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m @@ -211,7 +211,7 @@ if (topMessage && (!self.onlyShowTopMessageWhenCollapsed || !self.expanded)) { self.shortViewHeight.active = NO; - } else if (!topMessage && self.onlyShowTopMessageWhenCollapsed && self.expanded) { + } else if (!topMessage || (self.onlyShowTopMessageWhenCollapsed && self.expanded)) { self.shortViewHeight.active = YES; } }]; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 4ac5aaa0..ad0a833f 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -113,8 +113,8 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; - (void)pinATopViewController:(UIViewController *)viewController { self.statusBarHeightConstraint.active = NO; - id topGuide = viewController.topLayoutGuide; - self.statusBarBottomConstraint = [NSLayoutConstraint constraintWithItem:self.statusBarView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:topGuide attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; + id topGuide = viewController.view.safeAreaLayoutGuide; + self.statusBarBottomConstraint = [NSLayoutConstraint constraintWithItem:self.statusBarView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:topGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; self.statusBarBottomConstraint.active = YES; } From b36299b69592cf675670fa0ff4a23bf6708b4896 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 29 Jan 2021 17:07:57 -0500 Subject: [PATCH 25/51] latest and greatest --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 8 +++++++- .../Atomic/Molecules/Items/MoleculeTableViewCell.swift | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 8a8ca5fb..14d2e247 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -41,6 +41,12 @@ public typealias ActionBlock = () -> () return NSRange(location: 0, length: text?.count ?? 0) } + open override var text: String? { + didSet { + // TODO....???? + } + } + //------------------------------------------------------ // MARK: - Multi-Action Text //------------------------------------------------------ @@ -892,7 +898,7 @@ extension Label { if clauses.isEmpty { // Replace the last whitespace with \u{00A0} No-break space. if let start = text?.lastIndex(of: " "), let end = text?.index(after: start) { -// text?.replaceSubrange(start.. CGSize { + guard let molecule = molecule as? HeadlineBody else { return super.frame.size } + let cell = self + cell.frame = CGRect(x: 0, y: 0, width: targetSize.width, height: CGFloat(MAXFLOAT)) + cell.layoutIfNeeded() + + return CGSize(width: molecule.bounds.width + cell.contentView.directionalLayoutMargins.leading + cell.contentView.directionalLayoutMargins.trailing, height: molecule.bounds.height + cell.contentView.directionalLayoutMargins.top + cell.contentView.directionalLayoutMargins.bottom) + } } From 5c23e80429bcfc964d0d1ed8d8bae78b89111d82 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 1 Feb 2021 14:13:39 -0500 Subject: [PATCH 26/51] addressing intrinsic content issue --- .../Atomic/Atoms/Views/Label/Label.swift | 71 ++---------------- .../Items/MoleculeTableViewCell.swift | 10 --- .../HeadlineBody.swift | 73 ++++++++----------- 3 files changed, 35 insertions(+), 119 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 14d2e247..42827e67 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -38,13 +38,7 @@ public typealias ActionBlock = () -> () } public var getRange: NSRange { - return NSRange(location: 0, length: text?.count ?? 0) - } - - open override var text: String? { - didSet { - // TODO....???? - } + NSRange(location: 0, length: text?.count ?? 0) } //------------------------------------------------------ @@ -102,6 +96,10 @@ public typealias ActionBlock = () -> () accessibilityCustomActions = [] accessibilityTraits = .staticText +// if #available(iOS 14.0, *) { +// lineBreakStrategy = .init() +// } + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped)) tapGesture.numberOfTapsRequired = 1 addGestureRecognizer(tapGesture) @@ -918,64 +916,7 @@ extension Label { } } -extension Label { - - open func getSeparatedLines() -> [Any] { - - if lineBreakMode != .byWordWrapping { - lineBreakMode = .byWordWrapping - } - - var lines = [Any]() /* capacity: 10 */ - let wordSeparators = CharacterSet.whitespacesAndNewlines - var currentLine: String? = self.text - let textLength: Int = (self.text?.count ?? 0) - var rCurrentLine = NSRange(location: 0, length: textLength) - var rWhitespace = NSRange(location: 0, length: 0) - var rRemainingText = NSRange(location: 0, length: textLength) - var done: Bool = false - - while !done { - // determine the next whitespace word separator position - rWhitespace.location = rWhitespace.location + rWhitespace.length - rWhitespace.length = textLength - rWhitespace.location - rWhitespace = (self.text! as NSString).rangeOfCharacter(from: wordSeparators, options: .caseInsensitive, range: rWhitespace) - if rWhitespace.location == NSNotFound { - rWhitespace.location = textLength - done = true - } - let rTest = NSRange(location: rRemainingText.location, length: rWhitespace.location - rRemainingText.location) - let textTest: String = (self.text! as NSString).substring(with: rTest) - let fontAttributes: [String: Any]? = [NSAttributedString.Key.font.rawValue: font] - let maxWidth = (textTest as NSString).size(withAttributes: [NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue): font]).width - if maxWidth > self.bounds.size.width { - lines.append(currentLine?.trimmingCharacters(in: wordSeparators) ?? "") - rRemainingText.location = rCurrentLine.location + rCurrentLine.length - rRemainingText.length = textLength - rRemainingText.location - continue - } - rCurrentLine = rTest - currentLine = textTest - } - - lines.append(currentLine?.trimmingCharacters(in: wordSeparators) ?? "") - - return lines - } - - open var lastLineWidth: CGFloat { - - let lines: [Any] = self.getSeparatedLines() - - if !lines.isEmpty { - let lastLine: String = (lines.last as? String)! - let fontAttributes = [NSAttributedString.Key.font.rawValue: font] - return (lastLine as NSString).size(withAttributes: [NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue): font]).width - } - - return 0 - } -} + // MARK: - extension UITapGestureRecognizer { diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift index 621cbeee..92d04bed 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift @@ -42,14 +42,4 @@ import UIKit else { return 80 } return height } - - // TODO: Figure out height..... - open override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { - guard let molecule = molecule as? HeadlineBody else { return super.frame.size } - let cell = self - cell.frame = CGRect(x: 0, y: 0, width: targetSize.width, height: CGFloat(MAXFLOAT)) - cell.layoutIfNeeded() - - return CGSize(width: molecule.bounds.width + cell.contentView.directionalLayoutMargins.leading + cell.contentView.directionalLayoutMargins.trailing, height: molecule.bounds.height + cell.contentView.directionalLayoutMargins.top + cell.contentView.directionalLayoutMargins.bottom) - } } diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift index d111d1c5..8144fc03 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -19,19 +19,15 @@ open class HeadlineBody: View { // MARK: - Constraints //-------------------------------------------------- - var spaceBetweenLabelsConstant = PaddingOne + var spaceBetweenLabelsConstant = Padding.One var spaceBetweenLabels: NSLayoutConstraint? - var leftConstraintTitle: NSLayoutConstraint? - var rightConstraintTitle: NSLayoutConstraint? - var leftConstraintMessage: NSLayoutConstraint? - var rightConstraintMessage: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- func hasText() -> Bool { - return headlineLabel.hasText || messageLabel.hasText + headlineLabel.hasText || messageLabel.hasText } // MARK: - Styling @@ -57,13 +53,13 @@ open class HeadlineBody: View { func styleLandingPageHeader() { headlineLabel.setFontStyle(.Title2XLarge) messageLabel.setFontStyle(.RegularBodySmall) - spaceBetweenLabelsConstant = PaddingTwo + spaceBetweenLabelsConstant = Padding.Two } func stylePageHeader() { headlineLabel.setFontStyle(.BoldTitleLarge) messageLabel.setFontStyle(.RegularBodySmall) - spaceBetweenLabelsConstant = PaddingOne + spaceBetweenLabelsConstant = Padding.Two } func styleListItem() { @@ -86,48 +82,38 @@ open class HeadlineBody: View { super.setupView() backgroundColor = .clear - clipsToBounds = true + isAccessibilityElement = false + shouldGroupAccessibilityChildren = true + accessibilityElements = [headlineLabel, messageLabel] - let view = MVMCoreUICommonViewsUtility.commonView() - addSubview(view) - NSLayoutConstraint.constraintPinSubview(toSuperview: view) + addSubview(headlineLabel) + addSubview(messageLabel) - view.isAccessibilityElement = false - view.shouldGroupAccessibilityChildren = true - view.accessibilityElements = [headlineLabel, messageLabel] + headlineLabel.setContentCompressionResistancePriority(.required, for: .vertical) + messageLabel.setContentCompressionResistancePriority(.required, for: .vertical) - view.addSubview(headlineLabel) - view.addSubview(messageLabel) - - headlineLabel.setContentHuggingPriority(.required, for: .vertical) - messageLabel.setContentHuggingPriority(.required, for: .vertical) - view.setContentHuggingPriority(.required, for: .vertical) - - headlineLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + headlineLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true spaceBetweenLabels = messageLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: spaceBetweenLabelsConstant) spaceBetweenLabels?.isActive = true - leftConstraintTitle = headlineLabel.leftAnchor.constraint(equalTo: view.leftAnchor) - leftConstraintTitle?.isActive = true - - rightConstraintTitle = view.rightAnchor.constraint(equalTo: headlineLabel.rightAnchor) - rightConstraintTitle?.isActive = true - - leftConstraintMessage = messageLabel.leftAnchor.constraint(equalTo: view.leftAnchor) - leftConstraintMessage?.isActive = true - - rightConstraintMessage = view.rightAnchor.constraint(equalTo: messageLabel.rightAnchor) - rightConstraintMessage?.isActive = true - - view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor).isActive = true + NSLayoutConstraint.activate([ + headlineLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor), + messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor), + bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor) + ]) } open override func updateView(_ size: CGFloat) { super.updateView(size) + setSpacing() headlineLabel.updateView(size) messageLabel.updateView(size) - setSpacing() + let padding = MFStyler.defaultHorizontalPadding(forSize: size) * 2 + let maximumTextWidth = (size - (padding + Padding.Four)) + messageLabel.preferredMaxLayoutWidth = maximumTextWidth } //-------------------------------------------------- @@ -146,19 +132,18 @@ open class HeadlineBody: View { // MARK: - MoleculeViewProtocol //-------------------------------------------------- - public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 58 - } + public override class func estimatedHeight(with model: MoleculeModelProtocol, + _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 58 } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let headlineBodyModel = model as? HeadlineBodyModel else { return } + guard let model = model as? HeadlineBodyModel else { return } - style(with: headlineBodyModel.style) + style(with: model.style) - headlineLabel.setOptional(with: headlineBodyModel.headline, delegateObject, additionalData) - messageLabel.setOptional(with: headlineBodyModel.body, delegateObject, additionalData) + headlineLabel.setOptional(with: model.headline, delegateObject, additionalData) + messageLabel.setOptional(with: model.body, delegateObject, additionalData) } open override func reset() { From 3872591d50021e8d5eec91d239f963ef7efbdefa Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 1 Feb 2021 14:28:21 -0500 Subject: [PATCH 27/51] rm --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 42827e67..9a452e3f 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -96,10 +96,6 @@ public typealias ActionBlock = () -> () accessibilityCustomActions = [] accessibilityTraits = .staticText -// if #available(iOS 14.0, *) { -// lineBreakStrategy = .init() -// } - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped)) tapGesture.numberOfTapsRequired = 1 addGestureRecognizer(tapGesture) @@ -916,8 +912,6 @@ extension Label { } } - - // MARK: - extension UITapGestureRecognizer { From a5febf586c21ca6e2497a10263b01e1f20198566 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 1 Feb 2021 14:41:51 -0500 Subject: [PATCH 28/51] updated --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 7 ------- .../Molecules/VerticalCombinationViews/HeadlineBody.swift | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 9a452e3f..4cab6cd7 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -889,13 +889,6 @@ extension Label { /// Underlines the tappable region and stores the tap logic for interation. private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) { - if clauses.isEmpty { - // Replace the last whitespace with \u{00A0} No-break space. - if let start = text?.lastIndex(of: " "), let end = text?.index(after: start) { - text?.replaceSubrange(start.. Date: Mon, 1 Feb 2021 14:43:35 -0500 Subject: [PATCH 29/51] updateds --- .../Molecules/VerticalCombinationViews/HeadlineBody.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift index 93eace61..fb744f3e 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -59,7 +59,7 @@ open class HeadlineBody: View { func stylePageHeader() { headlineLabel.setFontStyle(.BoldTitleLarge) messageLabel.setFontStyle(.RegularBodySmall) - spaceBetweenLabelsConstant = Padding.Two + spaceBetweenLabelsConstant = Padding.One } func styleListItem() { @@ -90,7 +90,9 @@ open class HeadlineBody: View { addSubview(messageLabel) headlineLabel.setContentCompressionResistancePriority(.required, for: .vertical) + headlineLabel.setContentHuggingPriority(.required, for: .vertical) messageLabel.setContentCompressionResistancePriority(.required, for: .vertical) + messageLabel.setContentHuggingPriority(.required, for: .vertical) headlineLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true From 68d9473adcb407af57203938d26a723fbef25256 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 1 Feb 2021 14:45:20 -0500 Subject: [PATCH 30/51] comment --- .../Molecules/VerticalCombinationViews/HeadlineBody.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift index fb744f3e..703793f3 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -113,6 +113,8 @@ open class HeadlineBody: View { setSpacing() headlineLabel.updateView(size) messageLabel.updateView(size) + + // Provide the label additional size information to help calculate its intrinsic height. let padding = MFStyler.defaultHorizontalPadding(forSize: size) * 2 messageLabel.preferredMaxLayoutWidth = size - padding } From 674e63457cf32a97ab6f709e1d32627890224aeb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 3 Feb 2021 10:22:15 -0500 Subject: [PATCH 31/51] refactored protocol. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 ++++ .../AccessibilityModelProtocol.swift | 23 +++++++++++++++++++ .../MoleculeModelProtocol.swift | 8 +------ 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 MVMCoreUI/Atomic/Protocols/ModelProtocols/AccessibilityModelProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index f277003d..9f7816ec 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -105,6 +105,7 @@ 0A9D09212433796500D2E6C0 /* CarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091B2433796500D2E6C0 /* CarouselIndicatorModel.swift */; }; 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */; }; 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; }; + 0AA4D2E125CAEC72008DB32D /* AccessibilityModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */; }; 0AB000BA24BF63490090C5E7 /* ModalListPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */; }; 0AB000BC24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */; }; 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */; }; @@ -650,6 +651,7 @@ 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarouselIndicator.swift; sourceTree = ""; }; 0AA33B33239813C50067DD0F /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = ""; }; + 0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityModelProtocol.swift; sourceTree = ""; }; 0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalListPageTemplateModel.swift; sourceTree = ""; }; 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalStackPageTemplateModel.swift; sourceTree = ""; }; 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Extension.swift"; sourceTree = ""; }; @@ -1109,6 +1111,7 @@ 011B58EE23A2AA850085F53C /* ModelProtocols */ = { isa = PBXGroup; children = ( + 0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */, D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */, 014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */, D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */, @@ -2844,6 +2847,7 @@ 012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */, 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */, D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */, + 0AA4D2E125CAEC72008DB32D /* AccessibilityModelProtocol.swift in Sources */, C003506123AA94CD00B6AC29 /* Button.swift in Sources */, DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */, D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/AccessibilityModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/AccessibilityModelProtocol.swift new file mode 100644 index 00000000..a7508e39 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/AccessibilityModelProtocol.swift @@ -0,0 +1,23 @@ +// +// AccessibilityModelProtocol.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 2/3/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import Foundation + + +public protocol AccessibilityModelProtocol { + + var accessibilityIdentifier: String? { get set } +} + +public extension AccessibilityModelProtocol { + + var accessibilityIdentifier: String? { + get { nil } + set { } + } +} diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift index e554581d..4f0cd296 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift @@ -7,21 +7,15 @@ public enum MolecularError: Swift.Error { } -public protocol MoleculeModelProtocol: ModelProtocol { +public protocol MoleculeModelProtocol: ModelProtocol, AccessibilityModelProtocol { var moleculeName: String { get } var backgroundColor: Color? { get set } - var accessibilityIdentifier: String? { get set } } public extension MoleculeModelProtocol { var moleculeName: String { Self.identifier } - var accessibilityIdentifier: String? { - get { nil } - set { } - } - static var categoryName: String { "\(MoleculeModelProtocol.self)" } static var categoryCodingKey: String { "moleculeName" } From 303c28009ad2d5f49467b359c00d55d658af80b9 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 3 Feb 2021 10:54:08 -0500 Subject: [PATCH 32/51] remvoe --- MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift index 633eecc7..5ae68241 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift @@ -142,7 +142,6 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { backgroundColor = color.uiColor } - accessibilityIdentifier = model.accessibilityIdentifier enabledColor = (model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor disabledColor = (model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor From d6f97311a1e94df4cbd99984e3e579ff4b109240 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 3 Feb 2021 11:08:51 -0500 Subject: [PATCH 33/51] super --- MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift | 1 - MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 2 -- MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift | 1 - MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift | 1 - MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift | 1 - MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift | 2 -- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift | 1 - MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift | 1 - 8 files changed, 10 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift index 362d646f..9ced5f5a 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift @@ -52,7 +52,6 @@ import UIKit setTitle(model.title, for: .normal) accessibilityLabel = model.title - accessibilityIdentifier = model.accessibilityIdentifier setTitleColor((model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor, for: .normal) setTitleColor((model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor, for: .disabled) isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 6d2bebf0..f1fd5fed 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -406,8 +406,6 @@ import MVMCore self.fieldKey = fieldKey } - accessibilityIdentifier = model.accessibilityIdentifier - borderColor = (model.inverted ? model.invertedColor : model.borderColor).uiColor borderWidth = model.borderWidth diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index 3f905a32..e694e194 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -87,7 +87,6 @@ import UIKit self.delegateObject = delegateObject self.additionalData = additionalData guard let model = model as? HeartModel else { return } - accessibilityIdentifier = model.accessibilityIdentifier isSelected = model.isActive isEnabled = model.enabled updateAccessibilityLabel() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index 02ba1ea2..1d8c6960 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -85,7 +85,6 @@ open class RadioBox: Control, MFButtonProtocol { subTextLabel.text = model.subText isOutOfStock = model.strikethrough subTextLabelHeightConstraint?.isActive = (subTextLabel.text?.count ?? 0) == 0 - accessibilityIdentifier = model.accessibilityIdentifier if let color = model.selectedAccentColor?.uiColor { accentColor = color } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift index c438d6c3..4a596a35 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift @@ -62,7 +62,6 @@ open class RadioBoxes: View { FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) backgroundColor = model.backgroundColor?.uiColor - accessibilityIdentifier = model.accessibilityIdentifier registerCells() setHeight() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index d04d4999..1098ad3d 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -161,8 +161,6 @@ import UIKit guard let model = model as? RadioButtonModel else { return } - accessibilityIdentifier = model.accessibilityIdentifier - isSelected = model.state isEnabled = model.enabled RadioButtonSelectionHelper.setupForRadioButtonGroup(model, self, delegateObject: delegateObject) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index e661742a..e80f8813 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -62,7 +62,6 @@ open class RadioSwatch: Control, MFButtonProtocol { guard let model = model as? RadioSwatchModel else { return } self.delegateObject = delegateObject self.additionalData = additionalData - accessibilityIdentifier = model.accessibilityIdentifier bottomText.text = model.text isSelected = model.selected isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index 036dfaf2..5a5b6836 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -58,7 +58,6 @@ open class RadioSwatches: View { guard let model = model as? RadioSwatchesModel else { return } swatches = model.swatches FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) - accessibilityIdentifier = model.accessibilityIdentifier collectionView.reloadData() } From 57530bbfd90e5a847f1d134755bcd23d122c1991 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 8 Feb 2021 09:48:35 -0500 Subject: [PATCH 34/51] warning fix --- 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 abaa4130..50172397 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -74,7 +74,7 @@ import UIKit } open func modulesToListenFor() -> [String]? { - loadObject?.requestParameters?.allModules() as? [String] + loadObject?.requestParameters?.allModules() } @objc open func responseJSONUpdated(notification: Notification) { From 7d34634b03ade7052905622b709e540cfb595a21 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 8 Feb 2021 10:16:43 -0500 Subject: [PATCH 35/51] remove legacy number --- .../Atomic/Atoms/Views/LoadImageView.swift | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift index 0ad4c16b..aafddb00 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift @@ -234,7 +234,7 @@ } // Constrains the image view to be the size provided. Used to size it to the image to fix aspect fit defect. - func addConstraints(width: NSNumber?, height: NSNumber?, size: CGSize?) { + func addConstraints(width: CGFloat?, height: CGFloat?, size: CGSize?) { widthConstraint?.isActive = false heightConstraint?.isActive = false @@ -242,15 +242,15 @@ guard addSizeConstraintsForAspectRatio else { return } if let width = width, let height = height { - setHeight(height.cgfloat()) - setWidth(width.cgfloat()) + setHeight(height) + setWidth(width) } else if let width = width, let size = size { - setWidth(width.cgfloat()) + setWidth(width) heightConstraint = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: size.height/size.width) heightConstraint?.priority = UILayoutPriority(rawValue: 900) heightConstraint?.isActive = true } else if let height = height, let size = size { - setHeight(height.cgfloat()) + setHeight(height) widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: size.width/size.height) widthConstraint?.priority = UILayoutPriority(rawValue: 900) widthConstraint?.isActive = true @@ -288,7 +288,7 @@ if shouldLoadImage(withName: imageModel.image, width: width, height: height) { imageView.image = nil imageView.animatedImage = nil - loadImage(withName: imageModel.image, format: imageModel.imageFormat, width: width as NSNumber?, height: height as NSNumber?, customFallbackImage: imageModel.fallbackImage, localBundle: imageModel.localBundle) + loadImage(withName: imageModel.image, format: imageModel.imageFormat, width: width, height: height, customFallbackImage: imageModel.fallbackImage, localBundle: imageModel.localBundle) } if let contentMode = imageModel.contentMode { @@ -309,13 +309,13 @@ // MARK: - Load Methods //-------------------------------------------------- - public func loadImage(withName imageName: String?, format: String? = nil, width: NSNumber? = nil, height: NSNumber? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) { + public func loadImage(withName imageName: String?, format: String? = nil, width: CGFloat? = nil, height: CGFloat? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) { let completionBlock = completionHandler ?? defaultCompletionBlock() MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in self.currentImageName = imageName - self.currentImageWidth = width?.cgfloat() - self.currentImageHeight = height?.cgfloat() + self.currentImageWidth = width + self.currentImageHeight = height if MVMCoreCache.isHostedImage(imageName) { self.loadingSpinner.resumeSpinnerAfterDelay() self.loadingSpinnerHeightConstraint?.constant = self.spinnerHeight @@ -338,9 +338,9 @@ let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback") if let format = format, format.lowercased().contains("gif") { // Gifs aren't supported by default and need special handling - MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) + MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) } else { - MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) + MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) } }) } @@ -409,6 +409,6 @@ } public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) { - loadImage(withName: imageName, format: format, width: width, height: height, customFallbackImage: customFallbackImage, allowServerParameters: false, completionHandler: completionHandler) + loadImage(withName: imageName, format: format, width: width?.cgfloat(), height: height?.cgfloat(), customFallbackImage: customFallbackImage, allowServerParameters: false, completionHandler: completionHandler) } } From c473177832d345e6f243dab2c5b2663f37aa84cd Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 9 Feb 2021 13:18:29 -0500 Subject: [PATCH 36/51] image efficiency --- .../Molecules/OtherContainers/BGImageMoleculeModel.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/BGImageMoleculeModel.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/BGImageMoleculeModel.swift index 2dbfced5..56b77e3e 100644 --- a/MVMCoreUI/Atomic/Molecules/OtherContainers/BGImageMoleculeModel.swift +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/BGImageMoleculeModel.swift @@ -27,6 +27,12 @@ open class BGImageMoleculeModel: MoleculeContainerModel { if bottomPadding == nil { bottomPadding = PaddingDefaultVerticalSpacing3 } + if image.contentMode == nil { + image.contentMode = .scaleAspectFill + } + if image.imageFormat == nil { + image.imageFormat = "jpg" + } } private enum CodingKeys: String, CodingKey { From d333e449fec26df4a1293bbc0cc77b5a37e7d040 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 10 Feb 2021 12:40:23 -0500 Subject: [PATCH 37/51] move behavior --- MVMCoreUI/Atomic/Atoms/Views/Video.swift | 67 +------------------ .../Atomic/Atoms/Views/VideoDataManager.swift | 2 + MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 64 +++++++++++++++++- .../Atoms/Views/VideoPauseBehavior.swift | 35 ---------- MVMCoreUI/Behaviors/PageBehavior.swift | 10 ++- 5 files changed, 76 insertions(+), 102 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Video.swift b/MVMCoreUI/Atomic/Atoms/Views/Video.swift index 044b199d..6e0fffbc 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Video.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Video.swift @@ -16,10 +16,6 @@ open class Video: View { /// Used to track the state and respond.. private var stateKVOToken: NSKeyValueObservation? - private weak var pauseBehavior: BlockPageVisibilityBehavior? - - private weak var scrollBehavior: BlockPageScrolledBehavior? - private var visible = true open override func setupView() { @@ -54,9 +50,8 @@ open class Video: View { } // Handle pause behavior - addVisibleBehavoir(to: model, delegateObject: delegateObject) - addScrolledBehavoir(to: model, delegateObject: delegateObject) -// addForegroundCheck() + model.addVisibleBehavoir(for: self, delegateObject: delegateObject) + model.addScrollBehavoir(for: self, delegateObject: delegateObject) } /// Listens and responds to video loading state changes. @@ -102,62 +97,4 @@ open class Video: View { deinit { removeStateObserver() } - - /// Adds a behavoir to pause the video on page hidden behavoir and unpause if necessary on page shown. - open func addVisibleBehavoir(to model: VideoModel, delegateObject: MVMCoreUIDelegateObject?) { - let onShow = { [weak self] in - guard self?.visible == true, - let model = self?.model as? VideoModel, - model.autoPlay else { return } - model.videoDataManager.player?.play() - } - let onHide: () -> Void = { [weak self] in - guard self?.visible == true, - let model = self?.model as? VideoModel else { return } - model.videoDataManager.player?.pause() - } - - if pauseBehavior != nil { - pauseBehavior?.pageShownHandler = onShow - pauseBehavior?.pageHiddenHandler = onHide - } else { - guard let delegate = delegateObject?.moleculeDelegate, - let page = delegate as? PageProtocol, - var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } - var behavoirs = pageModel.behaviors ?? [] - let pauseBehavior = BlockPageVisibilityBehavior(with: onShow, onPageHiddenHandler: onHide) - behavoirs.append(pauseBehavior) - pageModel.behaviors = behavoirs - self.pauseBehavior = pauseBehavior - } - } - - open func addScrolledBehavoir(to model: VideoModel, delegateObject: MVMCoreUIDelegateObject?) { - - let onScroll = { [weak self] (scrollView: UIScrollView) in - // If visible to not visible, pause video. - // If not visible to visible, unpause if needed, add visible behavior - // visible == -// if !visible { -// pause -// } else { -// let model = self?.model as? VideoModel, -// model.autoPlay else { return } -// model.videoDataManager.player?.play() -// } - } - - if scrollBehavior != nil { - scrollBehavior?.pageScrolledHandler = onScroll - } else { - guard let delegate = delegateObject?.moleculeDelegate, - let page = delegate as? PageProtocol, - var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } - var behavoirs = pageModel.behaviors ?? [] - let scrollBehavior = BlockPageScrolledBehavior(with: onScroll) - behavoirs.append(scrollBehavior) - pageModel.behaviors = behavoirs - self.scrollBehavior = scrollBehavior - } - } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift index 754a6ef2..c616f4b6 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift @@ -127,6 +127,8 @@ import Foundation } private func removeMemoryWarningListener() { + guard let observer = memoryWarningListener else { return } + NotificationCenter.default.removeObserver(observer) memoryWarningListener = nil } diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index d335f375..0c1033e8 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -18,6 +18,9 @@ open class VideoModel: MoleculeModelProtocol { /// Keeps a reference to the video data. public var videoDataManager: VideoDataManager + + private weak var visibleBehavior: BlockPageVisibilityBehavior? + private weak var scrollBehavior: BlockPageScrolledBehavior? private enum CodingKeys: String, CodingKey { case moleculeName @@ -35,7 +38,6 @@ open class VideoModel: MoleculeModelProtocol { required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) video = try typeContainer.decode(String.self, forKey:.video) - videoDataManager = VideoDataManager(with: video) if let showControls = try typeContainer.decodeIfPresent(Bool.self, forKey: .showControls) { self.showControls = showControls } @@ -45,6 +47,7 @@ open class VideoModel: MoleculeModelProtocol { if let alwaysReset = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysReset) { self.alwaysReset = alwaysReset } + videoDataManager = VideoDataManager(with: video) } open func encode(to encoder: Encoder) throws { @@ -55,4 +58,63 @@ open class VideoModel: MoleculeModelProtocol { try container.encode(autoPlay, forKey: .autoPlay) try container.encode(alwaysReset, forKey: .alwaysReset) } + + // Temporary + func isVisible(view: UIView) -> Bool { + return true + } + + /// Adds a behavoir to pause the video on page hidden behavoir and unpause if necessary on page shown. + open func addVisibleBehavoir(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + let onShow = { [weak self] in + guard let self = self, + self.isVisible(view: view), + self.autoPlay else { return } + self.videoDataManager.player?.play() + } + let onHide: () -> Void = { [weak self] in + guard let self = self else { return } + self.videoDataManager.player?.pause() + } + + guard visibleBehavior == nil else { + visibleBehavior?.pageShownHandler = onShow + visibleBehavior?.pageHiddenHandler = onHide + return + } + + guard let delegate = delegateObject?.moleculeDelegate, + let page = delegate as? PageProtocol, + var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } + let pauseBehavior = BlockPageVisibilityBehavior(with: onShow, onPageHiddenHandler: onHide) + pageModel.add(behavior: pauseBehavior) + self.visibleBehavior = pauseBehavior + } + + /// Adds a behavoir to pause the video if scrolled off of the page and unpause if necessary if scrolled on. + open func addScrollBehavoir(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + + let onScroll = { [weak self] (scrollView: UIScrollView) in + // If visible to not visible, pause video. + // If not visible to visible, unpause if needed, add visible behavior + guard let self = self else { return } + if !self.isVisible(view: view) { + self.videoDataManager.player?.pause() + } else if self.autoPlay { + self.videoDataManager.player?.play() + } + } + + guard scrollBehavior == nil else { + scrollBehavior?.pageScrolledHandler = onScroll + return + } + + guard let delegate = delegateObject?.moleculeDelegate, + let page = delegate as? PageProtocol, + var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } + let scrollBehavior = BlockPageScrolledBehavior(with: onScroll) + pageModel.add(behavior: scrollBehavior) + self.scrollBehavior = scrollBehavior + } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift index 73293a8b..1524381e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift @@ -73,38 +73,3 @@ public class BlockPageScrolledBehavior: PageScrolledBehavior { pageScrolledHandler(scrollView) } } - - -public class VideoPauseBehavior: PageVisibilityBehavior { - - public static var identifier = "videoPause" - - public weak var videoModel: VideoModel? - public init(with videoModel: VideoModel) { - self.videoModel = videoModel - } - - private enum CodingKeys: String, CodingKey { - case behaviorName - } - - //MARK:- PageVisibilityBehavior - - public func onPageShown() { - // TODO: Only do this if attached to a visible view... - if videoModel?.autoPlay == true { - videoModel?.videoDataManager.player?.play() - } - } - - public func onPageHidden() { - videoModel?.videoDataManager.player?.pause() - } - - public required init(from decoder: Decoder) throws {} - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(behaviorName, forKey: .behaviorName) - } -} diff --git a/MVMCoreUI/Behaviors/PageBehavior.swift b/MVMCoreUI/Behaviors/PageBehavior.swift index ef80349e..59e41031 100644 --- a/MVMCoreUI/Behaviors/PageBehavior.swift +++ b/MVMCoreUI/Behaviors/PageBehavior.swift @@ -43,5 +43,13 @@ public protocol PageScrolledBehavior: PageBehaviorProtocol { public protocol PageBehaviorsTemplateProtocol { var behaviors: [PageBehaviorProtocol]? { get set } - + +} + +public extension PageBehaviorsTemplateProtocol { + mutating func add(behavior: PageBehaviorProtocol) { + var behavoirs = behaviors ?? [] + behavoirs.append(behavior) + self.behaviors = behavoirs + } } From 740e757fe4fb23e3c06ec88b55009e774b9626a8 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 10 Feb 2021 13:53:58 -0500 Subject: [PATCH 38/51] undelrine didn't show when rotating --- .../Atomic/Atoms/Buttons/Link/Link.swift | 30 ++++++++----------- .../Atomic/Atoms/Buttons/Link/LinkModel.swift | 4 +-- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift index 9ced5f5a..04140d6e 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift @@ -11,18 +11,18 @@ import UIKit @objcMembers open class Link: Button { //-------------------------------------------------- - // MARK: - Lifecycle + // MARK: - Draw //-------------------------------------------------- open override func draw(_ rect: CGRect) { - guard let textRect = titleLabel?.frame else { return } - - let context = UIGraphicsGetCurrentContext() + guard let textRect = titleLabel?.frame, + let context = UIGraphicsGetCurrentContext() + else { return } // Set line to the same color as the text if let color = titleLabel?.textColor?.cgColor { - context?.setStrokeColor(color) + context.setStrokeColor(color) } // x should be according to the text, not the button @@ -31,9 +31,9 @@ import UIKit // Line is 1 point below the text let y = textRect.origin.y + textRect.size.height + 1 - context?.move(to: CGPoint(x: x, y: y)) - context?.addLine(to: CGPoint(x: x + textRect.size.width, y: y)) - context?.strokePath() + context.move(to: CGPoint(x: x, y: y)) + context.addLine(to: CGPoint(x: x + textRect.size.width, y: y)) + context.strokePath() } open override var intrinsicContentSize: CGSize { @@ -67,16 +67,12 @@ extension Link { open override func updateView(_ size: CGFloat) { super.updateView(size) - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - - var width = size - if MVMCoreGetterUtility.fequal(a: Float.leastNormalMagnitude, b: Float(size)) { - width = MVMCoreUIUtility.getWidth() - } - - self.titleLabel?.font = MFStyler.fontB2(forWidth: width) + var width = size + if MVMCoreGetterUtility.fequal(a: Float.leastNormalMagnitude, b: Float(size)) { + width = MVMCoreUIUtility.getWidth() } + + titleLabel?.font = MFStyler.fontB2(forWidth: width) } open override func setupView() { diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift index 4519b030..f3b48bd4 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift @@ -14,9 +14,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode // MARK: - Properties //-------------------------------------------------- - public class var identifier: String { - return "link" - } + public class var identifier: String { "link" } public var backgroundColor: Color? public var accessibilityIdentifier: String? From a42063d925badd3c37b05dd06f55806e1c02b812 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 10 Feb 2021 17:08:30 -0500 Subject: [PATCH 39/51] accessibility --- .../Atomic/Molecules/HorizontalCombinationViews/TabBar.swift | 1 + .../Molecules/HorizontalCombinationViews/TabBarModel.swift | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift index 14b7cf94..4a6e3ca7 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift @@ -54,6 +54,7 @@ var tabs: [UITabBarItem] = [] for (index, tab) in model.tabs.enumerated() { let tabBarItem = UITabBarItem(title: tab.title, image: MVMCoreCache.shared()?.getImageFromRegisteredBundles(tab.image), tag: index) + tabBarItem.accessibilityLabel = tab.accessibilityText if #available(iOS 13.0, *) { } else { tabBarItem.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -3) diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift index 581b890e..fc3e9449 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBarModel.swift @@ -63,11 +63,13 @@ public class TabBarItemModel: Codable { var title: String? var image: String var action: ActionModelProtocol + var accessibilityText: String? private enum CodingKeys: String, CodingKey { case title case image case action + case accessibilityText } public init(with title: String?, image: String, action: ActionModelProtocol) { @@ -81,6 +83,7 @@ public class TabBarItemModel: Codable { title = try typeContainer.decodeIfPresent(String.self, forKey: .title) image = try typeContainer.decode(String.self, forKey: .image) action = try typeContainer.decodeModel(codingKey: .action) + accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) } public func encode(to encoder: Encoder) throws { @@ -88,5 +91,6 @@ public class TabBarItemModel: Codable { try container.encodeIfPresent(title, forKey: .title) try container.encode(image, forKey: .image) try container.encodeModel(action, forKey: .action) + try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) } } From f74fbc059c8b598f0645e943c8f2c9cfbb2f8ff3 Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Thu, 11 Feb 2021 21:53:54 +0530 Subject: [PATCH 40/51] implementing feedback --- .../Molecules/VerticalCombinationViews/HeadlineBody.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift index f5c0c565..b3aed7fc 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -56,19 +56,19 @@ open class HeadlineBody: View { spaceBetweenLabelsConstant = Padding.Two } - func stylePageHeader() { + public func stylePageHeader() { headlineLabel.setFontStyle(.BoldTitleLarge) messageLabel.setFontStyle(.RegularBodySmall) spaceBetweenLabelsConstant = Padding.One } - func styleListItem() { + public func styleListItem() { headlineLabel.setFontStyle(.BoldBodySmall) messageLabel.setFontStyle(.RegularBodySmall) spaceBetweenLabelsConstant = 0 } - func styleListItemDivider() { + public func styleListItemDivider() { headlineLabel.setFontStyle(.BoldTitleMedium) messageLabel.setFontStyle(.RegularBodySmall) spaceBetweenLabelsConstant = 0 From c2f1cd200d6228c1dfb4651208346cf1895b8dc4 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 11 Feb 2021 17:18:06 -0500 Subject: [PATCH 41/51] review comments --- MVMCoreUI.xcodeproj/project.pbxproj | 12 ++- MVMCoreUI/Atomic/Atoms/Views/Video.swift | 13 ++-- .../Atomic/Atoms/Views/VideoDataManager.swift | 4 +- MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 72 +++++++++++------- .../Atoms/Views/VideoPauseBehavior.swift | 75 ------------------- MVMCoreUI/Behaviors/PageBehavior.swift | 14 +++- .../PageScrolledClosureBehavior.swift | 33 ++++++++ .../PageVisibilityClosureBehavior.swift | 40 ++++++++++ MVMCoreUI/OtherHandlers/CoreUIObject.swift | 2 + MVMCoreUI/Utility/MVMCoreUIUtility.h | 8 ++ MVMCoreUI/Utility/MVMCoreUIUtility.m | 45 +++++++++++ 11 files changed, 200 insertions(+), 118 deletions(-) delete mode 100644 MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift create mode 100644 MVMCoreUI/Behaviors/PageScrolledClosureBehavior.swift create mode 100644 MVMCoreUI/Behaviors/PageVisibilityClosureBehavior.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 693b46ce..5c490b5a 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -347,6 +347,8 @@ D23EA800247EBD6C00D60C34 /* LabelBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */; }; D23EA802247EBED400D60C34 /* ImageBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */; }; D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; + D24918F625D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D24918F525D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift */; }; + D24918FA25D5ADBB00CAB4B1 /* PageScrolledClosureBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D24918F925D5ADBA00CAB4B1 /* PageScrolledClosureBehavior.swift */; }; D2509ED12472ED9B001BFB9D /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */; }; D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */; }; D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; }; @@ -408,7 +410,6 @@ D29C559025C095210082E7D6 /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C558F25C095210082E7D6 /* Video.swift */; }; D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559225C0992D0082E7D6 /* VideoModel.swift */; }; D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559525C099630082E7D6 /* VideoDataManager.swift */; }; - D29C559C25C20D6D0082E7D6 /* VideoPauseBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C559B25C20D6D0082E7D6 /* VideoPauseBehavior.swift */; }; D29C94D5242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */; }; D29DF0D121E404D4003B2FB9 /* MVMCoreUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */; }; @@ -898,6 +899,8 @@ D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelBarButtonItem.swift; sourceTree = ""; }; D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBarButtonItem.swift; sourceTree = ""; }; D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; + D24918F525D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageVisibilityClosureBehavior.swift; sourceTree = ""; }; + D24918F925D5ADBA00CAB4B1 /* PageScrolledClosureBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageScrolledClosureBehavior.swift; sourceTree = ""; }; D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = ""; }; D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationImageButtonModel.swift; sourceTree = ""; }; D253BB9B245874F8002DE544 /* BGImageMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageMolecule.swift; sourceTree = ""; }; @@ -957,7 +960,6 @@ D29C558F25C095210082E7D6 /* Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Video.swift; sourceTree = ""; }; D29C559225C0992D0082E7D6 /* VideoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoModel.swift; sourceTree = ""; }; D29C559525C099630082E7D6 /* VideoDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDataManager.swift; sourceTree = ""; }; - D29C559B25C20D6D0082E7D6 /* VideoPauseBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPauseBehavior.swift; sourceTree = ""; }; D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUICommonViewsUtility+Extension.swift"; sourceTree = ""; }; D29DF0CC21E404D4003B2FB9 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUI.h; sourceTree = ""; }; @@ -1240,6 +1242,8 @@ children = ( 27F973522466074500CAB5C5 /* PageBehavior.swift */, 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */, + D24918F525D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift */, + D24918F925D5ADBA00CAB4B1 /* PageScrolledClosureBehavior.swift */, ); path = Behaviors; sourceTree = ""; @@ -2038,7 +2042,6 @@ AA07EA902510A442009A2AE3 /* StarModel.swift */, AA07EA922510A451009A2AE3 /* Star.swift */, D29C559525C099630082E7D6 /* VideoDataManager.swift */, - D29C559B25C20D6D0082E7D6 /* VideoPauseBehavior.swift */, D29C559225C0992D0082E7D6 /* VideoModel.swift */, D29C558F25C095210082E7D6 /* Video.swift */, ); @@ -2672,7 +2675,6 @@ 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, BB54C5212434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift in Sources */, D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */, - D29C559C25C20D6D0082E7D6 /* VideoPauseBehavior.swift in Sources */, 0A775F2824893937009EFB58 /* ThreeHeadlineBodyLinkModel.swift in Sources */, 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */, D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */, @@ -2707,6 +2709,7 @@ 94C2D9A523872C350006CF46 /* LabelAttributeFontModel.swift in Sources */, 011D958724042492000E3791 /* FormFieldProtocol.swift in Sources */, 011D95AF2407266E000E3791 /* RadioButtonModel.swift in Sources */, + D24918F625D5AD8E00CAB4B1 /* PageVisibilityClosureBehavior.swift in Sources */, D20492A624329CE200A5EED6 /* LoadImageView.swift in Sources */, 017BEB7F23676E870024EF95 /* MoleculeObjectMapping.swift in Sources */, D274CA332236A78900B01B62 /* FooterView.swift in Sources */, @@ -2792,6 +2795,7 @@ 012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */, D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */, + D24918FA25D5ADBB00CAB4B1 /* PageScrolledClosureBehavior.swift in Sources */, 0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */, D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */, 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/Video.swift b/MVMCoreUI/Atomic/Atoms/Views/Video.swift index 6e0fffbc..f869ab06 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Video.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Video.swift @@ -10,13 +10,8 @@ import AVKit open class Video: View { public let videoViewController = AVPlayerViewController() - // TODO: reuse. - private let videoQueue = DispatchQueue(label: "serial.video.queue") - /// Used to track the state and respond.. private var stateKVOToken: NSKeyValueObservation? - - private var visible = true open override func setupView() { super.setupView() @@ -29,6 +24,10 @@ open class Video: View { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? VideoModel else { return } + if let controller = delegateObject?.moleculeDelegate as? UIViewController { + controller.addChild(videoViewController) + videoViewController.didMove(toParent: controller) + } videoViewController.showsPlaybackControls = model.showControls videoViewController.player = model.videoDataManager.player addStateObserver() @@ -50,8 +49,8 @@ open class Video: View { } // Handle pause behavior - model.addVisibleBehavoir(for: self, delegateObject: delegateObject) - model.addScrollBehavoir(for: self, delegateObject: delegateObject) + model.addVisibleBehavior(for: self, delegateObject: delegateObject) + model.addScrollBehavior(for: self, delegateObject: delegateObject) } /// Listens and responds to video loading state changes. diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift index c616f4b6..046ba3bb 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift @@ -7,9 +7,11 @@ // import Foundation +import AVFoundation @objcMembers open class VideoDataManager: NSObject { + /// The state of the video. @objc public enum State: Int { case none case loading @@ -22,7 +24,7 @@ import Foundation // Thread Safe video state handling. private var _videoState = State.none - private let videoStatueQueue = DispatchQueue(label: "concurrent.video.state.queue", attributes: .concurrent) + private let videoStatueQueue = DispatchQueue(label: "com.vzw.mvmcoreui.videoDataManager.state", attributes: .concurrent) /// The state of the video. Use KVO to listen for state changes. @objc public var videoState: State { diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index 0c1033e8..c64b13e5 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -15,12 +15,15 @@ open class VideoModel: MoleculeModelProtocol { public var showControls = false public var autoPlay = true public var alwaysReset = false + + /// When the video is halted because it is no longer visible + private var halted = false /// Keeps a reference to the video data. public var videoDataManager: VideoDataManager - private weak var visibleBehavior: BlockPageVisibilityBehavior? - private weak var scrollBehavior: BlockPageScrolledBehavior? + private weak var visibleBehavior: PageVisibilityClosureBehavior? + private weak var scrollBehavior: PageScrolledClosureBehavior? private enum CodingKeys: String, CodingKey { case moleculeName @@ -59,22 +62,38 @@ open class VideoModel: MoleculeModelProtocol { try container.encode(alwaysReset, forKey: .alwaysReset) } - // Temporary - func isVisible(view: UIView) -> Bool { - return true + private func haltVideo() { + guard !halted else { return } + halted = true + print("halted \(video)") + videoDataManager.player?.pause() } - /// Adds a behavoir to pause the video on page hidden behavoir and unpause if necessary on page shown. - open func addVisibleBehavoir(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + private func unhaltVideo() { + guard halted else { return } + halted = false + print("unhalted \(video)") + guard videoDataManager.videoState == .loaded else { return } + if alwaysReset { + // Always start video at the beginning. + videoDataManager.player?.seek(to: .zero) + } + if autoPlay { + videoDataManager.player?.play() + } + } + + /// Adds a behavior to pause the video on page hidden behavior and unpause if necessary on page shown. + open func addVisibleBehavior(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { let onShow = { [weak self] in guard let self = self, - self.isVisible(view: view), - self.autoPlay else { return } - self.videoDataManager.player?.play() + let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view, + MVMCoreUIUtility.isView(view, visibleIn: containingView) else { return } + self.unhaltVideo() } let onHide: () -> Void = { [weak self] in guard let self = self else { return } - self.videoDataManager.player?.pause() + self.haltVideo() } guard visibleBehavior == nil else { @@ -83,25 +102,24 @@ open class VideoModel: MoleculeModelProtocol { return } - guard let delegate = delegateObject?.moleculeDelegate, - let page = delegate as? PageProtocol, - var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } - let pauseBehavior = BlockPageVisibilityBehavior(with: onShow, onPageHiddenHandler: onHide) - pageModel.add(behavior: pauseBehavior) + guard var delegate = delegateObject?.behaviorTemplateDelegate else { return } + let pauseBehavior = PageVisibilityClosureBehavior(with: onShow, onPageHiddenHandler: onHide) + delegate.add(behavior: pauseBehavior) self.visibleBehavior = pauseBehavior } - /// Adds a behavoir to pause the video if scrolled off of the page and unpause if necessary if scrolled on. - open func addScrollBehavoir(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + /// Adds a behavior to pause the video if scrolled off of the page and unpause if necessary if scrolled on. + open func addScrollBehavior(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { let onScroll = { [weak self] (scrollView: UIScrollView) in // If visible to not visible, pause video. // If not visible to visible, unpause if needed, add visible behavior - guard let self = self else { return } - if !self.isVisible(view: view) { - self.videoDataManager.player?.pause() - } else if self.autoPlay { - self.videoDataManager.player?.play() + guard let self = self, + let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return } + if !MVMCoreUIUtility.isView(view, visibleIn: containingView) { + self.haltVideo() + } else { + self.unhaltVideo() } } @@ -110,11 +128,9 @@ open class VideoModel: MoleculeModelProtocol { return } - guard let delegate = delegateObject?.moleculeDelegate, - let page = delegate as? PageProtocol, - var pageModel = page.pageModel as? PageBehaviorsTemplateProtocol else { return } - let scrollBehavior = BlockPageScrolledBehavior(with: onScroll) - pageModel.add(behavior: scrollBehavior) + guard var delegate = delegateObject?.behaviorTemplateDelegate else { return } + let scrollBehavior = PageScrolledClosureBehavior(with: onScroll) + delegate.add(behavior: scrollBehavior) self.scrollBehavior = scrollBehavior } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift deleted file mode 100644 index 1524381e..00000000 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoPauseBehavior.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// VideoPauseBehavior.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 1/27/21. -// Copyright © 2021 Verizon Wireless. All rights reserved. -// - -import Foundation - -public class BlockPageVisibilityBehavior: PageVisibilityBehavior { - - public static var identifier = "blockPageVisibilityBehavior" - - public var pageShownHandler: () -> Void - public var pageHiddenHandler: () -> Void - - public init(with onPageShownHandler: @escaping () -> Void, onPageHiddenHandler: @escaping () -> Void) { - self.pageShownHandler = onPageShownHandler - self.pageHiddenHandler = onPageHiddenHandler - } - - private enum CodingKeys: String, CodingKey { - case behaviorName - } - - // This class is not meant to be decoded and encoded really. - public required init(from decoder: Decoder) throws { - pageShownHandler = {} - pageHiddenHandler = {} - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(behaviorName, forKey: .behaviorName) - } - - //MARK:- PageVisibilityBehavior - public func onPageShown() { - pageShownHandler() - } - - public func onPageHidden() { - pageHiddenHandler() - } -} - -public class BlockPageScrolledBehavior: PageScrolledBehavior { - - public static var identifier = "blockPageScrolledBehavior" - - public var pageScrolledHandler: (_ scrollView: UIScrollView) -> Void - - public init(with onPageScrolledHandler: @escaping (_ scrollView: UIScrollView) -> Void) { - self.pageScrolledHandler = onPageScrolledHandler - } - - private enum CodingKeys: String, CodingKey { - case behaviorName - } - - // This class is not meant to be decoded and encoded really. - public required init(from decoder: Decoder) throws { - pageScrolledHandler = { (scrollView) in } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(behaviorName, forKey: .behaviorName) - } - - public func pageScrolled(scrollView: UIScrollView) { - pageScrolledHandler(scrollView) - } -} diff --git a/MVMCoreUI/Behaviors/PageBehavior.swift b/MVMCoreUI/Behaviors/PageBehavior.swift index 59e41031..aaf915a1 100644 --- a/MVMCoreUI/Behaviors/PageBehavior.swift +++ b/MVMCoreUI/Behaviors/PageBehavior.swift @@ -48,8 +48,16 @@ public protocol PageBehaviorsTemplateProtocol { public extension PageBehaviorsTemplateProtocol { mutating func add(behavior: PageBehaviorProtocol) { - var behavoirs = behaviors ?? [] - behavoirs.append(behavior) - self.behaviors = behavoirs + var newBehaviors = behaviors ?? [] + newBehaviors.append(behavior) + self.behaviors = newBehaviors + } +} + +public extension MVMCoreUIDelegateObject { + weak var behaviorTemplateDelegate: (PageBehaviorsTemplateProtocol & NSObjectProtocol)? { + get { + return (moleculeDelegate as? PageProtocol)?.pageModel as? (PageBehaviorsTemplateProtocol & NSObjectProtocol) + } } } diff --git a/MVMCoreUI/Behaviors/PageScrolledClosureBehavior.swift b/MVMCoreUI/Behaviors/PageScrolledClosureBehavior.swift new file mode 100644 index 00000000..c43d4ecc --- /dev/null +++ b/MVMCoreUI/Behaviors/PageScrolledClosureBehavior.swift @@ -0,0 +1,33 @@ +// +// PageScrolledClosureBehavior.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 2/11/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class PageScrolledClosureBehavior: PageScrolledBehavior { + + public static var identifier = "pageScrolledClosureBehavior" + + public var pageScrolledHandler: (_ scrollView: UIScrollView) -> Void + + public init(with onPageScrolledHandler: @escaping (_ scrollView: UIScrollView) -> Void) { + self.pageScrolledHandler = onPageScrolledHandler + } + + // This class is not meant to be decoded and encoded really. + public required init(from decoder: Decoder) throws { + throw ModelRegistry.Error.decoderOther(message: "PageScrolledClosureBehavior does not decode.") + } + + public func encode(to encoder: Encoder) throws { + throw ModelRegistry.Error.decoderOther(message: "PageScrolledClosureBehavior does not encode.") + } + + public func pageScrolled(scrollView: UIScrollView) { + pageScrolledHandler(scrollView) + } +} diff --git a/MVMCoreUI/Behaviors/PageVisibilityClosureBehavior.swift b/MVMCoreUI/Behaviors/PageVisibilityClosureBehavior.swift new file mode 100644 index 00000000..f5ecd82c --- /dev/null +++ b/MVMCoreUI/Behaviors/PageVisibilityClosureBehavior.swift @@ -0,0 +1,40 @@ +// +// PageVisibilityClosureBehavior.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 2/11/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class PageVisibilityClosureBehavior: PageVisibilityBehavior { + + public static var identifier = "pageVisibilityClosureBehavior" + + public var pageShownHandler: () -> Void + public var pageHiddenHandler: () -> Void + + public init(with onPageShownHandler: @escaping () -> Void, onPageHiddenHandler: @escaping () -> Void) { + self.pageShownHandler = onPageShownHandler + self.pageHiddenHandler = onPageHiddenHandler + } + + // This class is not meant to be decoded and encoded really. + public required init(from decoder: Decoder) throws { + throw ModelRegistry.Error.decoderOther(message: "PageVisibilityClosureBehavior does not decode.") + } + + public func encode(to encoder: Encoder) throws { + throw ModelRegistry.Error.decoderOther(message: "PageVisibilityClosureBehavior does not encode.") + } + + //MARK:- PageVisibilityBehavior + public func onPageShown() { + pageShownHandler() + } + + public func onPageHidden() { + pageHiddenHandler() + } +} diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index 3a4d2cbc..90532f98 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -13,6 +13,7 @@ import UIKit public var globalTopAlertDelegate: MVMCoreGlobalTopAlertDelegateProtocol? open override func defaultInitialSetup() { + loadHandler = MVMCoreLoadHandler() cache = MVMCoreCache() sessionHandler = MVMCoreSessionTimeHandler() actionHandler = MVMCoreActionHandler() @@ -21,5 +22,6 @@ import UIKit loggingDelegate = MVMCoreUILoggingHandler() moleculeMap = MoleculeObjectMapping() MoleculeObjectMapping.registerObjects() + clientParameterRegistry = ClientParameterRegistry() } } diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.h b/MVMCoreUI/Utility/MVMCoreUIUtility.h index 4bf6e8d5..74b19d0e 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.h +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.h @@ -37,6 +37,14 @@ NS_ASSUME_NONNULL_BEGIN /// Checks if the view or any descendents of the view is currently focused for voice over. + (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view; ++ (BOOL)isView:(nonnull UIView *)view visibleIn:(nonnull UIView *)rootView; + ++ (BOOL)isViewVisibleInParent:(nonnull UIView *)view; + ++ (BOOL)doesView:(nonnull UIView *)overlappingView cover:(nonnull UIView *)view; + ++ (BOOL)isViewTransparent:(nonnull UIView *)view; + #pragma mark - Setters + (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom; diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index fed0bb30..fad2378c 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -96,6 +96,51 @@ return containsFocus; } ++ (BOOL)isView:(nonnull UIView *)view visibleIn:(nonnull UIView *)rootView { + return [view isDescendantOfView:rootView] && [self isViewVisibleInParent:view]; +} + +// Climb up the tree. Break early. ++ (BOOL)isViewVisibleInParent:(nonnull UIView *)view { + UIView *superview = view.superview; + UIView *ancestor = view; + // Begin climbing its ancestor views, checking if it remains visible in *each* of their bounds and if the children at views closer to the front block the visibility of this view. + while (superview) { + if (superview.clipsToBounds && !CGRectIntersectsRect(superview.bounds, [view convertRect:view.bounds toView:superview])) { + return false; + } else { + // Check the superview's children up to the common ancestor. (Reworking the ancestor would put us in an infinite loop and we want to ignore the children with a lower z-order.) + for (UIView *subview in [superview.subviews reverseObjectEnumerator]) { + if (subview == ancestor) { + break; + } else if ([self doesView:subview cover:view]) { + return false; + } + } + } + ancestor = superview; + superview = superview.superview; + } + return true; +} + +// Climb down the tree. ++ (BOOL)doesView:(nonnull UIView *)overlappingView cover:(nonnull UIView *)view { + if (![self isViewTransparent:overlappingView] && CGRectContainsRect(overlappingView.bounds, [view convertRect:view.bounds toView:overlappingView])) { + return true; + } + for (UIView *subview in overlappingView.subviews) { + if ([self doesView:subview cover:view]) { + return true; + } + } + return false; +} + ++ (BOOL)isViewTransparent:(nonnull UIView *)view { + return view.alpha < 1 || view.backgroundColor == nil || CGColorGetAlpha(view.backgroundColor.CGColor) < 1; +} + #pragma mark - Setters + (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom { From 23a8c2c1baba15bd295bc32492b73fe7556964c0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 11 Feb 2021 17:51:09 -0500 Subject: [PATCH 42/51] conflict update --- MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift | 8 ++++---- MVMCoreUI/BaseControllers/ScrollingViewController.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift index 046ba3bb..a4ba9235 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoDataManager.swift @@ -12,7 +12,7 @@ import AVFoundation @objcMembers open class VideoDataManager: NSObject { /// The state of the video. - @objc public enum State: Int { + @objc public enum VideoState: Int { case none case loading case loaded @@ -23,13 +23,13 @@ import AVFoundation public var player: AVPlayer? // Thread Safe video state handling. - private var _videoState = State.none + private var _videoState = VideoState.none private let videoStatueQueue = DispatchQueue(label: "com.vzw.mvmcoreui.videoDataManager.state", attributes: .concurrent) /// The state of the video. Use KVO to listen for state changes. - @objc public var videoState: State { + @objc public var videoState: VideoState { get { - var state = State.none + var state = VideoState.none videoStatueQueue.sync { state = _videoState } diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift index 9a9a42e3..19c9d001 100644 --- a/MVMCoreUI/BaseControllers/ScrollingViewController.swift +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -73,7 +73,7 @@ open class ScrollingViewController: ViewController { scrollView.flashScrollIndicators() } - public func scrollViewDidScroll(_ scrollView: UIScrollView) { + open func scrollViewDidScroll(_ scrollView: UIScrollView) { executeBehaviors { (behavior: PageScrolledBehavior) in behavior.pageScrolled(scrollView: scrollView) } From 744398b7b8ac68cf1583054c09613dcc566c2f15 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 11 Feb 2021 19:44:24 -0500 Subject: [PATCH 43/51] Register class, remove debug --- MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 3 +-- MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index c64b13e5..8bc3d827 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -65,14 +65,13 @@ open class VideoModel: MoleculeModelProtocol { private func haltVideo() { guard !halted else { return } halted = true - print("halted \(video)") + guard videoDataManager.videoState == .loaded else { return } videoDataManager.player?.pause() } private func unhaltVideo() { guard halted else { return } halted = false - print("unhalted \(video)") guard videoDataManager.videoState == .loaded else { return } if alwaysReset { // Always start video at the beginning. diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 559b611b..e4d3a59a 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -106,6 +106,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: RadioButtonLabel.self, viewModelClass: RadioButtonLabelModel.self) MoleculeObjectMapping.shared()?.register(viewClass: WebView.self, viewModelClass: WebViewModel.self) MoleculeObjectMapping.shared()?.register(viewClass: LoadingSpinner.self, viewModelClass: LoadingSpinnerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Video.self, viewModelClass: VideoModel.self) // MARK:- Horizontal Combination Molecules MoleculeObjectMapping.shared()?.register(viewClass: StringAndMoleculeView.self, viewModelClass: StringAndMoleculeModel.self) @@ -151,6 +152,7 @@ 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: BGVideoImageMolecule.self, viewModelClass: BGVideoImageMoleculeModel.self) MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionHeader.self, viewModelClass: MoleculeSectionHeaderModel.self) MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionFooter.self, viewModelClass: MoleculeSectionFooterModel.self) From 0793498e3f0a4038e8543792bf5e9da1a767c6a2 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 11 Feb 2021 20:31:17 -0500 Subject: [PATCH 44/51] Active listener --- MVMCoreUI/Atomic/Atoms/Views/Video.swift | 1 + MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Video.swift b/MVMCoreUI/Atomic/Atoms/Views/Video.swift index f869ab06..92ff3ecf 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Video.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Video.swift @@ -51,6 +51,7 @@ open class Video: View { // Handle pause behavior model.addVisibleBehavior(for: self, delegateObject: delegateObject) model.addScrollBehavior(for: self, delegateObject: delegateObject) + model.addActiveListener(for: self, delegateObject: delegateObject) } /// Listens and responds to video loading state changes. diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index 8bc3d827..9b1d4f71 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -24,6 +24,8 @@ open class VideoModel: MoleculeModelProtocol { private weak var visibleBehavior: PageVisibilityClosureBehavior? private weak var scrollBehavior: PageScrolledClosureBehavior? + private var activeListener: Any? + private var resignActiveListener: Any? private enum CodingKeys: String, CodingKey { case moleculeName @@ -113,9 +115,8 @@ open class VideoModel: MoleculeModelProtocol { let onScroll = { [weak self] (scrollView: UIScrollView) in // If visible to not visible, pause video. // If not visible to visible, unpause if needed, add visible behavior - guard let self = self, - let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return } - if !MVMCoreUIUtility.isView(view, visibleIn: containingView) { + guard let self = self else { return } + if !MVMCoreUIUtility.isView(view, visibleIn: scrollView) { self.haltVideo() } else { self.unhaltVideo() @@ -132,4 +133,33 @@ open class VideoModel: MoleculeModelProtocol { delegate.add(behavior: scrollBehavior) self.scrollBehavior = scrollBehavior } + + open func addActiveListener(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + removeActiveListener() + + guard let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return } + resignActiveListener = NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in + self?.haltVideo() + } + activeListener = NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in + if MVMCoreUIUtility.isView(view, visibleIn: containingView) { + self?.unhaltVideo() + } + } + } + + private func removeActiveListener() { + if let observer = activeListener { + NotificationCenter.default.removeObserver(observer) + activeListener = nil + } + if let observer = resignActiveListener { + NotificationCenter.default.removeObserver(observer) + resignActiveListener = nil + } + } + + deinit { + removeActiveListener() + } } From ff96fd284a716ae21c88737d784821fe8a2b57a6 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 12 Feb 2021 11:46:05 -0500 Subject: [PATCH 45/51] load image needed --- MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift index aafddb00..8d8ef49e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift @@ -21,7 +21,8 @@ public var addSizeConstraintsForAspectRatio = true public var shouldNotifyDelegateOnUpdate = true - + public var shouldNotifyDelegateOnDefaultSizeChange = false + // Allows for a view to hardcode which height to use if there is none in the json. var imageWidth: CGFloat? var imageHeight: CGFloat? @@ -228,7 +229,7 @@ let widthWillChange = !MVMCoreGetterUtility.cgfequal(widthConstraint?.constant ?? 0, width ?? 0) let heightWillChange = !MVMCoreGetterUtility.cgfequal(heightConstraint?.constant ?? 0, height ?? 0) - let sizeWillChange = (width == nil || height == nil) && !(size?.equalTo(imageView.image?.size ?? CGSize.zero) ?? false) + let sizeWillChange = shouldNotifyDelegateOnDefaultSizeChange && (width == nil || height == nil) && !(size?.equalTo(imageView.image?.size ?? CGSize.zero) ?? false) let heightChangeFromSpinner = (heightConstraint?.isActive ?? false) ? false : ((height ?? size?.height) ?? 0) < loadingSpinnerHeightConstraint?.constant ?? CGFloat.leastNormalMagnitude return widthWillChange || heightWillChange || sizeWillChange || heightChangeFromSpinner } From 0f0bc68f1eda8ad17e65abb3f84e78e6ee664984 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 12 Feb 2021 14:19:33 -0500 Subject: [PATCH 46/51] restructure video --- .../Atomic/Atoms/Views/LoadImageView.swift | 5 +- MVMCoreUI/Atomic/Atoms/Views/Video.swift | 35 +++--- MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 114 +++++++++++++----- 3 files changed, 109 insertions(+), 45 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift index aafddb00..8d8ef49e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift @@ -21,7 +21,8 @@ public var addSizeConstraintsForAspectRatio = true public var shouldNotifyDelegateOnUpdate = true - + public var shouldNotifyDelegateOnDefaultSizeChange = false + // Allows for a view to hardcode which height to use if there is none in the json. var imageWidth: CGFloat? var imageHeight: CGFloat? @@ -228,7 +229,7 @@ let widthWillChange = !MVMCoreGetterUtility.cgfequal(widthConstraint?.constant ?? 0, width ?? 0) let heightWillChange = !MVMCoreGetterUtility.cgfequal(heightConstraint?.constant ?? 0, height ?? 0) - let sizeWillChange = (width == nil || height == nil) && !(size?.equalTo(imageView.image?.size ?? CGSize.zero) ?? false) + let sizeWillChange = shouldNotifyDelegateOnDefaultSizeChange && (width == nil || height == nil) && !(size?.equalTo(imageView.image?.size ?? CGSize.zero) ?? false) let heightChangeFromSpinner = (heightConstraint?.isActive ?? false) ? false : ((height ?? size?.height) ?? 0) < loadingSpinnerHeightConstraint?.constant ?? CGFloat.leastNormalMagnitude return widthWillChange || heightWillChange || sizeWillChange || heightChangeFromSpinner } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Video.swift b/MVMCoreUI/Atomic/Atoms/Views/Video.swift index f869ab06..76b8231b 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Video.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Video.swift @@ -9,6 +9,7 @@ import AVKit open class Video: View { public let videoViewController = AVPlayerViewController() + public var delegateObject: MVMCoreUIDelegateObject? /// Used to track the state and respond.. private var stateKVOToken: NSKeyValueObservation? @@ -21,9 +22,22 @@ open class Video: View { videoViewController.videoGravity = .resizeAspectFill } + /// Checks if the video is visible in the molecule delegate + open func isVisibleInDelegate() -> Bool { + guard let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return true } + return isVisible(in: containingView) + } + + /// Checks if the video is visible in the passed in view + open func isVisible(in view: UIView) -> Bool { + return MVMCoreUIUtility.isView(self, visibleIn: view) + } + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.delegateObject = delegateObject super.set(with: model, delegateObject, additionalData) guard let model = model as? VideoModel else { return } + if let controller = delegateObject?.moleculeDelegate as? UIViewController { controller.addChild(videoViewController) videoViewController.didMove(toParent: controller) @@ -31,26 +45,19 @@ open class Video: View { videoViewController.showsPlaybackControls = model.showControls videoViewController.player = model.videoDataManager.player addStateObserver() + model.addVisibilityHalting(for: self, delegateObject: delegateObject) + switch (model.videoDataManager.videoState) { case .none: // Begin loading the video model.videoDataManager.loadVideo() case .loaded: - // Video loaded - if model.alwaysReset { - // Always start video at the beginning. - model.videoDataManager.player?.seek(to: .zero) - } - if model.autoPlay { - model.videoDataManager.player?.play() - } + guard isVisibleInDelegate() else { return } + // Video loaded, unhalt it if necessary. + model.halted = false default: break } - - // Handle pause behavior - model.addVisibleBehavior(for: self, delegateObject: delegateObject) - model.addScrollBehavior(for: self, delegateObject: delegateObject) } /// Listens and responds to video loading state changes. @@ -73,10 +80,10 @@ open class Video: View { MVMCoreDispatchUtility.performSyncBlock(onMainThread: { // Play the video self.videoViewController.player = item.player - if model.autoPlay { + if !model.halted && model.autoPlay && self.isVisibleInDelegate() { item.player?.play() + UIAccessibility.post(notification: .screenChanged, argument: self) } - UIAccessibility.post(notification: .screenChanged, argument: self) }) case .failed: if let errorObject = item.loadFailedError { diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index c64b13e5..7000bdd5 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -15,15 +15,36 @@ open class VideoModel: MoleculeModelProtocol { public var showControls = false public var autoPlay = true public var alwaysReset = false + weak public var view: Video? /// When the video is halted because it is no longer visible - private var halted = false - + public var halted: Bool = false { + didSet { + guard halted != oldValue, + videoDataManager.videoState == .loaded else { return } + if halted { + videoDataManager.player?.pause() + printThat(string: "halted") + } else { + printThat(string: "unhalted") + if alwaysReset { + // Always start video at the beginning. + videoDataManager.player?.seek(to: .zero) + } + if autoPlay { + videoDataManager.player?.play() + } + } + } + } + /// Keeps a reference to the video data. public var videoDataManager: VideoDataManager private weak var visibleBehavior: PageVisibilityClosureBehavior? private weak var scrollBehavior: PageScrolledClosureBehavior? + private var activeListener: Any? + private var resignActiveListener: Any? private enum CodingKeys: String, CodingKey { case moleculeName @@ -62,38 +83,48 @@ open class VideoModel: MoleculeModelProtocol { try container.encode(alwaysReset, forKey: .alwaysReset) } - private func haltVideo() { - guard !halted else { return } - halted = true - print("halted \(video)") - videoDataManager.player?.pause() + func printThat(string: String) { + var newString = "sss" + string + " model:\(Unmanaged.passUnretained(self).toOpaque())" + if var view: UIView = self.view { + newString = newString + " view:\(Unmanaged.passUnretained(view).toOpaque())" + while (view as? BGVideoImageMolecule) == nil { + if let superView = view.superview { + view = superView + } else { + break + } + } + if let title = (((view as? BGVideoImageMolecule)?.view as? FooterView)?.view as? TwoButtonView)?.primaryButton.title(for: .normal) { + newString = newString + " \(title)" + } + } + print(newString) } - private func unhaltVideo() { - guard halted else { return } + open func addVisibilityHalting(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + printThat(string: "before adding") + // reset halted = false - print("unhalted \(video)") - guard videoDataManager.videoState == .loaded else { return } - if alwaysReset { - // Always start video at the beginning. - videoDataManager.player?.seek(to: .zero) - } - if autoPlay { - videoDataManager.player?.play() - } + (view.model as? VideoModel)?.view = nil + self.view = view + printThat(string: "after adding") + + addVisibleBehavior(for: view, delegateObject: delegateObject) + addScrollBehavior(for: view, delegateObject: delegateObject) + addActiveListener(for: view, delegateObject: delegateObject) } /// Adds a behavior to pause the video on page hidden behavior and unpause if necessary on page shown. open func addVisibleBehavior(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + let onShow = { [weak self] in guard let self = self, - let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view, - MVMCoreUIUtility.isView(view, visibleIn: containingView) else { return } - self.unhaltVideo() + let view = self.view, + view.isVisibleInDelegate() else { return } + self.halted = false } let onHide: () -> Void = { [weak self] in - guard let self = self else { return } - self.haltVideo() + self?.halted = true } guard visibleBehavior == nil else { @@ -115,12 +146,8 @@ open class VideoModel: MoleculeModelProtocol { // If visible to not visible, pause video. // If not visible to visible, unpause if needed, add visible behavior guard let self = self, - let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return } - if !MVMCoreUIUtility.isView(view, visibleIn: containingView) { - self.haltVideo() - } else { - self.unhaltVideo() - } + let view = self.view else { return } + self.halted = !view.isVisible(in: scrollView) } guard scrollBehavior == nil else { @@ -133,4 +160,33 @@ open class VideoModel: MoleculeModelProtocol { delegate.add(behavior: scrollBehavior) self.scrollBehavior = scrollBehavior } + + open func addActiveListener(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { + removeActiveListener() + + guard let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return } + resignActiveListener = NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in + self?.halted = true + } + activeListener = NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in + if MVMCoreUIUtility.isView(view, visibleIn: containingView) { + self?.halted = false + } + } + } + + private func removeActiveListener() { + if let observer = activeListener { + NotificationCenter.default.removeObserver(observer) + activeListener = nil + } + if let observer = resignActiveListener { + NotificationCenter.default.removeObserver(observer) + resignActiveListener = nil + } + } + + deinit { + removeActiveListener() + } } From 4be36bf185ffaef0e5fa07304994b1bfbd657e6c Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 12 Feb 2021 14:32:09 -0500 Subject: [PATCH 47/51] timing issue --- MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index 7000bdd5..2ff3cdb1 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -104,11 +104,10 @@ open class VideoModel: MoleculeModelProtocol { open func addVisibilityHalting(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { printThat(string: "before adding") // reset - halted = false (view.model as? VideoModel)?.view = nil self.view = view printThat(string: "after adding") - + halted = false addVisibleBehavior(for: view, delegateObject: delegateObject) addScrollBehavior(for: view, delegateObject: delegateObject) addActiveListener(for: view, delegateObject: delegateObject) From 742ea295b948dbb08b53d4bcb7675504903a4d77 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 12 Feb 2021 14:55:14 -0500 Subject: [PATCH 48/51] ugh timing workaround --- MVMCoreUI/Atomic/Atoms/Views/Video.swift | 1 + MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Video.swift b/MVMCoreUI/Atomic/Atoms/Views/Video.swift index 76b8231b..06474f95 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Video.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Video.swift @@ -35,6 +35,7 @@ open class Video: View { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.delegateObject = delegateObject + (self.model as? VideoModel)?.view = nil super.set(with: model, delegateObject, additionalData) guard let model = model as? VideoModel else { return } diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index 2ff3cdb1..68ec423a 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -103,8 +103,6 @@ open class VideoModel: MoleculeModelProtocol { open func addVisibilityHalting(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { printThat(string: "before adding") - // reset - (view.model as? VideoModel)?.view = nil self.view = view printThat(string: "after adding") halted = false From 7e5aa638ddca2a4d15fda0c805935c072f57a01c Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 12 Feb 2021 15:17:14 -0500 Subject: [PATCH 49/51] active fix --- MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index 68ec423a..ac97eba0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -161,14 +161,14 @@ open class VideoModel: MoleculeModelProtocol { open func addActiveListener(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { removeActiveListener() - guard let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return } resignActiveListener = NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in self?.halted = true } activeListener = NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (notification) in - if MVMCoreUIUtility.isView(view, visibleIn: containingView) { - self?.halted = false - } + guard let self = self, + let view = self.view, + view.isVisibleInDelegate() else { return } + self.halted = false } } From b5a00cb33fd92c78a9811442e67e019d2c12e125 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 12 Feb 2021 15:28:38 -0500 Subject: [PATCH 50/51] debug cleanup --- MVMCoreUI/Atomic/Atoms/Views/Video.swift | 9 ++++---- MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift | 22 ------------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Video.swift b/MVMCoreUI/Atomic/Atoms/Views/Video.swift index 06474f95..baee04f6 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Video.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Video.swift @@ -9,7 +9,7 @@ import AVKit open class Video: View { public let videoViewController = AVPlayerViewController() - public var delegateObject: MVMCoreUIDelegateObject? + private weak var containingView: UIView? /// Used to track the state and respond.. private var stateKVOToken: NSKeyValueObservation? @@ -24,7 +24,7 @@ open class Video: View { /// Checks if the video is visible in the molecule delegate open func isVisibleInDelegate() -> Bool { - guard let containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view else { return true } + guard let containingView = containingView else { return true } return isVisible(in: containingView) } @@ -34,11 +34,12 @@ open class Video: View { } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - self.delegateObject = delegateObject + /// Detach the view from it's previous model before setting. (self.model as? VideoModel)?.view = nil + containingView = (delegateObject?.moleculeDelegate as? UIViewController)?.view super.set(with: model, delegateObject, additionalData) - guard let model = model as? VideoModel else { return } + guard let model = model as? VideoModel else { return } if let controller = delegateObject?.moleculeDelegate as? UIViewController { controller.addChild(videoViewController) videoViewController.didMove(toParent: controller) diff --git a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift index ac97eba0..1d079599 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/VideoModel.swift @@ -24,9 +24,7 @@ open class VideoModel: MoleculeModelProtocol { videoDataManager.videoState == .loaded else { return } if halted { videoDataManager.player?.pause() - printThat(string: "halted") } else { - printThat(string: "unhalted") if alwaysReset { // Always start video at the beginning. videoDataManager.player?.seek(to: .zero) @@ -83,28 +81,8 @@ open class VideoModel: MoleculeModelProtocol { try container.encode(alwaysReset, forKey: .alwaysReset) } - func printThat(string: String) { - var newString = "sss" + string + " model:\(Unmanaged.passUnretained(self).toOpaque())" - if var view: UIView = self.view { - newString = newString + " view:\(Unmanaged.passUnretained(view).toOpaque())" - while (view as? BGVideoImageMolecule) == nil { - if let superView = view.superview { - view = superView - } else { - break - } - } - if let title = (((view as? BGVideoImageMolecule)?.view as? FooterView)?.view as? TwoButtonView)?.primaryButton.title(for: .normal) { - newString = newString + " \(title)" - } - } - print(newString) - } - open func addVisibilityHalting(for view: Video, delegateObject: MVMCoreUIDelegateObject?) { - printThat(string: "before adding") self.view = view - printThat(string: "after adding") halted = false addVisibleBehavior(for: view, delegateObject: delegateObject) addScrollBehavior(for: view, delegateObject: delegateObject) From 0aa4e690ae5196c9d549fba3c25cbb8c3963284a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 12 Feb 2021 16:50:28 -0500 Subject: [PATCH 51/51] move flag set --- 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 50172397..ecb4089a 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -348,8 +348,8 @@ import UIKit // First update should be explicit (hence the zero check) if needsUpdateUI || (previousScreenSize != .zero && screenSizeChanged()) { - updateViews() needsUpdateUI = false + updateViews() } previousScreenSize = view.bounds.size;