From 54912489990d274ee1d6741cafd4e3ba52b2b608 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 24 Aug 2020 19:31:11 -0400 Subject: [PATCH 01/54] webView.scrollView.contentSize --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 1ce2b123..159d758d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -132,7 +132,7 @@ extension WebView : WKUIDelegate { so webView.isLoading to check load finished state */ if !webView.isLoading { - webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in + webView.evaluateJavaScript("webView.scrollView.contentSize", completionHandler: { [weak self] (result, error) in guard let self = self else { return } From 61c3231965a0e857b6db86b9d2e6ccacd2623dee Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 24 Aug 2020 21:43:44 -0400 Subject: [PATCH 02/54] fix --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 159d758d..b1696a1d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -132,16 +132,17 @@ extension WebView : WKUIDelegate { so webView.isLoading to check load finished state */ if !webView.isLoading { - webView.evaluateJavaScript("webView.scrollView.contentSize", completionHandler: { [weak self] (result, error) in + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in guard let self = self else { return } - if let height = result as? CGFloat { + let scrollHeight = self.webView?.scrollView.contentSize.height ?? 44.0 + if let height = result as? CGFloat, + height < scrollHeight { self.webViewHeight?.constant = height } else { //if failed to get height from javascript, using scrollview.contensize's height - let scrollHeight = self.webView?.scrollView.contentSize.height - self.webViewHeight?.constant = scrollHeight ?? 44 + self.webViewHeight?.constant = scrollHeight } self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) }) From db851b00a9106c2a5ec1011d02d34ca0d6392be8 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 24 Aug 2020 22:09:03 -0400 Subject: [PATCH 03/54] fiix --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 32 ++++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index b1696a1d..374f0497 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -128,23 +128,31 @@ extension WebView : WKUIDelegate { if !dynamicHeight { return } - /* was using "document.readyState" to check the state, while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". + /* + //TODO: Check and remove the comment + was using "document.readyState" to check the state, + while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". so webView.isLoading to check load finished state */ if !webView.isLoading { - webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in - guard let self = self else { + webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in + if complete == nil { return } - let scrollHeight = self.webView?.scrollView.contentSize.height ?? 44.0 - if let height = result as? CGFloat, - height < scrollHeight { - self.webViewHeight?.constant = height - } else { - //if failed to get height from javascript, using scrollview.contensize's height - self.webViewHeight?.constant = scrollHeight - } - self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in + guard let self = self else { + return + } + let scrollHeight = self.webView?.scrollView.contentSize.height ?? 44.0 + if let height = result as? CGFloat, + height < scrollHeight { + self.webViewHeight?.constant = height + } else { + //if failed to get height from javascript, using scrollview.contensize's height + self.webViewHeight?.constant = scrollHeight + } + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + }) }) } } From d2f334067af3f00b9fe3ad71db289c3807c37151 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 24 Aug 2020 22:14:05 -0400 Subject: [PATCH 04/54] fix --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 42 +++++++++++----------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 374f0497..68c3b5fc 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -129,32 +129,30 @@ extension WebView : WKUIDelegate { return } /* - //TODO: Check and remove the comment + //TODO: Check and remove the comment was using "document.readyState" to check the state, while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". so webView.isLoading to check load finished state */ - if !webView.isLoading { - webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in - if complete == nil { - return - } - webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in - guard let self = self else { - return - } - let scrollHeight = self.webView?.scrollView.contentSize.height ?? 44.0 - if let height = result as? CGFloat, - height < scrollHeight { - self.webViewHeight?.constant = height - } else { - //if failed to get height from javascript, using scrollview.contensize's height - self.webViewHeight?.constant = scrollHeight - } - self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) - }) - }) - } + webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in + if complete == nil { + return + } + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in + guard let self = self else { + return + } + let scrollHeight = self.webView?.scrollView.contentSize.height ?? 44.0 + if let height = result as? CGFloat, + height < scrollHeight { + self.webViewHeight?.constant = height + } else { + //if failed to get height from javascript, using scrollview.contensize's height + self.webViewHeight?.constant = scrollHeight + } + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + }) + }) } From f67b8a644d93ff25490a5667843c5793c0134f67 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 25 Aug 2020 18:00:07 -0400 Subject: [PATCH 05/54] fix --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 68c3b5fc..533725ff 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -142,7 +142,7 @@ extension WebView : WKUIDelegate { guard let self = self else { return } - let scrollHeight = self.webView?.scrollView.contentSize.height ?? 44.0 + let scrollHeight = webView.scrollView.contentSize.height if let height = result as? CGFloat, height < scrollHeight { self.webViewHeight?.constant = height From f3c3ba9799ee4014d33fa916bd4962688b4d28b8 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 25 Aug 2020 19:32:48 -0400 Subject: [PATCH 06/54] remove code --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 39 +++++++++++----------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 533725ff..54805815 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -134,25 +134,26 @@ extension WebView : WKUIDelegate { while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". so webView.isLoading to check load finished state */ - webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in - if complete == nil { - return - } - webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in - guard let self = self else { - return - } - let scrollHeight = webView.scrollView.contentSize.height - if let height = result as? CGFloat, - height < scrollHeight { - self.webViewHeight?.constant = height - } else { - //if failed to get height from javascript, using scrollview.contensize's height - self.webViewHeight?.constant = scrollHeight - } - self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) - }) - }) + if !webView.isLoading { + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in + guard let self = self else { + return + } + MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in + let scrollHeight = webView.scrollView.contentSize.height + guard let self = self else { + return + } + if let height = result as? CGFloat { + self.webViewHeight?.constant = height + } else { + //if failed to get height from javascript, using scrollview.contensize's height + self.webViewHeight?.constant = scrollHeight + } + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + }) + }) + } } From dce1c5cdf2276c129e2fc222f659b0dfe2ddcf63 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 25 Aug 2020 19:34:25 -0400 Subject: [PATCH 07/54] cleanup --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 54805815..3de42935 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -129,7 +129,6 @@ extension WebView : WKUIDelegate { return } /* - //TODO: Check and remove the comment was using "document.readyState" to check the state, while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". so webView.isLoading to check load finished state @@ -140,17 +139,16 @@ extension WebView : WKUIDelegate { return } MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in - let scrollHeight = webView.scrollView.contentSize.height guard let self = self else { return } - if let height = result as? CGFloat { - self.webViewHeight?.constant = height - } else { - //if failed to get height from javascript, using scrollview.contensize's height - self.webViewHeight?.constant = scrollHeight - } - self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + if let height = result as? CGFloat { + self.webViewHeight?.constant = height + } else { + //if failed to get height from javascript, using scrollview.contensize's height + self.webViewHeight?.constant = webView.scrollView.contentSize.height + } + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) }) }) } From 4c0d38e39886af5b9695c004810996d65280d3b2 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 11 Sep 2020 14:16:11 -0400 Subject: [PATCH 08/54] func added. formating and commenting --- .../Atomic/Atoms/Views/Label/Label.swift | 33 +++++++++++++++---- .../Label/LabelAttributeActionModel.swift | 22 +++++++++++-- .../Label/LabelAttributeColorModel.swift | 1 - .../Views/Label/LabelAttributeFontModel.swift | 2 -- .../Label/LabelAttributeImageModel.swift | 3 +- .../Views/Label/LabelAttributeModel.swift | 11 ++++--- .../LabelAttributeStrikeThroughModel.swift | 14 +++++++- .../Label/LabelAttributeUnderlineModel.swift | 9 ++++- .../Atomic/Atoms/Views/Label/LabelModel.swift | 2 -- 9 files changed, 74 insertions(+), 23 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 73de7760..748013f0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -257,8 +257,10 @@ public typealias ActionBlock = () -> () switch labelModel.textAlignment { case .center: textAlignment = .center + case .right: textAlignment = .right + default: textAlignment = .left } @@ -290,7 +292,7 @@ public typealias ActionBlock = () -> () if let color = labelModel.textColor { textColor = color.uiColor } - + if let attributes = labelModel.attributes, let labelText = text { let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.updateSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor]) @@ -359,7 +361,7 @@ public typealias ActionBlock = () -> () continue } } - + attributedText = attributedString originalAttributedString = attributedText } @@ -503,11 +505,11 @@ public typealias ActionBlock = () -> () textColor = .mvmBlack setScale(scale) } - + //------------------------------------------------------ // MARK: - 2.0 Styling Methods //------------------------------------------------------ - + @objc public func styleH1(_ scale: Bool) { MFStyler.styleLabelH1(self, genericScaling: false) setScale(scale) @@ -548,6 +550,23 @@ public typealias ActionBlock = () -> () setScale(scale) } + /// Will remove the values contained in attributedText. + func clearAttributes() { + + guard let labelText = text, + let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: labelText.count)) + else { return } + + let attributedString = NSMutableAttributedString(string: labelText) + + for attribute in attributes { + attributedString.removeAttribute(attribute.key, range: NSRange(location: 0, length: labelText.count)) + } + + attributedText = attributedString + } + + @objc public func updateView(_ size: CGFloat) { scaleSize = size as NSNumber @@ -835,7 +854,7 @@ 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) } @@ -920,7 +939,7 @@ extension Label { } @objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) { - + for clause in clauses { if action.hash == clause.accessibilityID { clause.performAction() @@ -930,7 +949,7 @@ extension Label { } open override func accessibilityActivate() -> Bool { - + guard let accessibleActions = accessibilityCustomActions else { return false } for clause in clauses { diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift index 3e615cdd..f9920dcc 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift @@ -6,23 +6,39 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import UIKit open class LabelAttributeActionModel: LabelAttributeModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + override public class var identifier: String { return "action" } + var action: ActionModelProtocol - + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(_ location: Int, _ length: Int, action: ActionModelProtocol) { self.action = action super.init(location, length) } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case action } - + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) action = try typeContainer.decodeModel(codingKey: .action) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift index 86275d6d..b86251c3 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift @@ -6,7 +6,6 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import UIKit @objcMembers public class LabelAttributeColorModel: LabelAttributeModel { //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift index 862f8f9b..59dbac3d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift @@ -6,8 +6,6 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import UIKit - @objcMembers public class LabelAttributeFontModel: LabelAttributeModel { //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift index 1980353c..70cd90fd 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift @@ -6,13 +6,12 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import UIKit class LabelAttributeImageModel: LabelAttributeModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - + override public class var identifier: String { return "image" } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift index 77f7fde6..ae5f15b7 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift @@ -6,7 +6,6 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import Foundation @objcMembers open class LabelAttributeModel: ModelProtocol { //-------------------------------------------------- @@ -20,7 +19,7 @@ import Foundation public static var categoryCodingKey: String { return "type" } - + public class var identifier: String { return "" } @@ -31,12 +30,16 @@ import Foundation var location: Int var length: Int - + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(_ location: Int, _ length: Int) { self.location = location self.length = length } - + //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift index 59552e18..e84c1804 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift @@ -6,16 +6,28 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import UIKit @objcMembers public class LabelAttributeStrikeThroughModel: LabelAttributeModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + override public class var identifier: String { return "strikethrough" } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { try super.init(from: decoder) } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift index d052686d..597b2491 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift @@ -6,13 +6,20 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import UIKit @objcMembers public class LabelAttributeUnderlineModel: LabelAttributeModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + override public class var identifier: String { return "underline" } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { try super.init(from: decoder) } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift index 4ec23ad6..6b120d0c 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift @@ -7,8 +7,6 @@ // -import Foundation - @objcMembers public class LabelModel: MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties From a8e13263e6f224e76301ea565dbc29b6dafb6a7d Mon Sep 17 00:00:00 2001 From: Damodaram Date: Mon, 14 Sep 2020 16:56:38 +0530 Subject: [PATCH 09/54] added action attribute for radiobutton --- MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift | 5 ++++- MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index 371d3f7a..239a368b 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -9,7 +9,7 @@ import UIKit -@objcMembers open class RadioButton: Control { +@objcMembers open class RadioButton: Control,MFButtonProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -99,6 +99,9 @@ import UIKit } else { isSelected = !isSelected } + if let actionModel = radioModel?.action, isSelected { + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + } _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift index ea1a4627..7d50cf56 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift @@ -26,6 +26,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { public var baseValue: AnyHashable? public var groupName: String = FormValidator.defaultGroupName public var fieldKey: String? + public var action: ActionModelProtocol? //-------------------------------------------------- // MARK: - Keys @@ -39,6 +40,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { case fieldValue case fieldKey case groupName + case action } //-------------------------------------------------- @@ -81,6 +83,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { self.groupName = groupName } fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public func encode(to encoder: Encoder) throws { @@ -92,5 +95,6 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + try container.encodeModelIfPresent(action, forKey: .action) } } From c76911e76e4a4adb1661beeaf444861edea9b2bb Mon Sep 17 00:00:00 2001 From: Damodaram Date: Mon, 14 Sep 2020 20:07:56 +0530 Subject: [PATCH 10/54] review changes. --- MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index 239a368b..e1f3ab93 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -9,7 +9,7 @@ import UIKit -@objcMembers open class RadioButton: Control,MFButtonProtocol { +@objcMembers open class RadioButton: Control, MFButtonProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -100,7 +100,7 @@ import UIKit isSelected = !isSelected } if let actionModel = radioModel?.action, isSelected { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) } _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) setNeedsDisplay() From d3a9e64b55704a3fff74d56bc16ca01be46f4f00 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 14 Sep 2020 21:39:01 -0400 Subject: [PATCH 11/54] molecular top alert quick draft --- MVMCoreUI.xcodeproj/project.pbxproj | 16 ++ MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 4 + .../Atomic/TopNotificationModel.swift | 80 ++++++ .../MVMCoreUITopAlertExpandableView.m | 15 ++ .../TopAlert/MVMCoreUITopAlertMainView.h | 3 + .../TopAlert/MVMCoreUITopAlertMainView.m | 11 + .../MVMCoreUITopAlertView+Extension.swift | 252 ++++++++++++++++++ MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 25 +- 8 files changed, 397 insertions(+), 9 deletions(-) create mode 100644 MVMCoreUI/TopAlert/Atomic/TopNotificationModel.swift create mode 100644 MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b61c3aa1..f9a0cdb3 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -301,6 +301,8 @@ D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */; }; D20923592450ECE00044AD09 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20923582450ECE00044AD09 /* TableView.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; + D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; }; + D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; @@ -782,6 +784,8 @@ D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerModelBase.swift; sourceTree = ""; }; D20923582450ECE00044AD09 /* TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; + D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = ""; }; + D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; @@ -1258,6 +1262,14 @@ path = FourColumn; sourceTree = ""; }; + D20C7007250BF9440095B21C /* Atomic */ = { + isa = PBXGroup; + children = ( + D20C7008250BF99B0095B21C /* TopNotificationModel.swift */, + ); + path = Atomic; + sourceTree = ""; + }; D20FFFB42451E32100A31DA2 /* Device */ = { isa = PBXGroup; children = ( @@ -1757,6 +1769,8 @@ D29DF11E21E6851E003B2FB9 /* TopAlert */ = { isa = PBXGroup; children = ( + D20C7007250BF9440095B21C /* Atomic */, + D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */, D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */, D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */, D29DF12321E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h */, @@ -2234,6 +2248,7 @@ D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */, 011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */, BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */, + D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */, D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */, 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, @@ -2463,6 +2478,7 @@ D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */, AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, + D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */, 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */, BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */, AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index a08347c7..a598b113 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -232,6 +232,10 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: LockUpsPlanNames.self, viewModelClass: LockUpsPlanNamesModel.self) MoleculeObjectMapping.shared()?.register(viewClass: LockupsPlanSMLXL.self, viewModelClass: LockupsPlanSMLXLModel.self) + // MARK: - Top Notifications + MoleculeObjectMapping.shared()?.register(viewClass: MVMCoreUITopAlertMainView.self, viewModelClass: NotificationModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MVMCoreUITopAlertExpandableView.self, viewModelClass: CollapsableNotificationModel.self) + // MARK:- Helper models try? ModelRegistry.register(RuleRequiredModel.self) try? ModelRegistry.register(RuleAnyRequiredModel.self) diff --git a/MVMCoreUI/TopAlert/Atomic/TopNotificationModel.swift b/MVMCoreUI/TopAlert/Atomic/TopNotificationModel.swift new file mode 100644 index 00000000..d6900c85 --- /dev/null +++ b/MVMCoreUI/TopAlert/Atomic/TopNotificationModel.swift @@ -0,0 +1,80 @@ +// +// TopNotification.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/11/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class TopNotificationModel: Codable { + public var type: String + public var priority = Operation.QueuePriority.normal + public var molecule: MoleculeModelProtocol + public var persistent = false + public var dismissTime = 5 + public var pages: [String]? + public var analyticsData: JSONValueDictionary? + + private enum CodingKeys: String, CodingKey { + case type + case priority + case molecule + case persistent + case dismissTime + case pages + case analyticsData + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(with type: String, molecule: MoleculeModelProtocol, priority: Operation.QueuePriority = .normal, persistent: Bool = false, dismissTime: Int = 5, pages: [String]? = nil, analyticsData: JSONValueDictionary? = nil) { + self.type = type + self.molecule = molecule + self.priority = priority + self.persistent = persistent + self.dismissTime = dismissTime + self.pages = pages + self.analyticsData = analyticsData + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + type = try typeContainer.decode(String.self, forKey: .type) + molecule = try typeContainer.decodeModel(codingKey: .molecule) + if let priorityPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .priority) { + let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue + let scaledPercent = (priorityPercent / 100.0) * Float(scale) + self.priority = Operation.QueuePriority(rawValue: Operation.QueuePriority.veryLow.rawValue + Int(scaledPercent)) ?? .normal + } + if let persistent = try typeContainer.decodeIfPresent(Bool.self, forKey: .persistent) { + self.persistent = persistent + } + if let dismissTime = try typeContainer.decodeIfPresent(Int.self, forKey: .dismissTime) { + self.dismissTime = dismissTime + } + pages = try typeContainer.decodeIfPresent([String].self, forKey: .pages) + analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(type, forKey: .type) + try container.encodeModel(molecule, forKey: .molecule) + + let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue + let priorityPercent = Int((Float(priority.rawValue - Operation.QueuePriority.veryLow.rawValue) / Float(scale)) * 100.0) + try container.encode(priorityPercent, forKey: .priority) + try container.encode(persistent, forKey: .persistent) + try container.encode(dismissTime, forKey: .dismissTime) + try container.encodeIfPresent(pages, forKey: .pages) + try container.encodeIfPresent(analyticsData, forKey: .analyticsData) + } +} diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m index 55841e3f..7f03fe25 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m @@ -76,6 +76,21 @@ return self; } +- (nonnull instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + self.clipsToBounds = YES; + _collapseAutomaticallyAfterExpanded = YES; + self.viewToLayout = MVMCoreUITopAlertView.sharedGlobal.superview; + self.expanded = NO; + [self setupTopMessage:nil]; + MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] initWithFrame:frame]; + [self setupTopAlertWithButton:topAlertWithButton]; + } + return self; +} + + - (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id )animationDelegate viewToLayout:(nonnull UIView *)viewTolayout { if (self = [self init]) { self.animationDelegate = animationDelegate; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h index 0f75ad9c..137ac168 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h @@ -37,6 +37,9 @@ - (void)setupButtonWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; - (void)setupButtonWithButtonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler; +// Setters for close button. +- (void)setupCloseButton:(BOOL)closeButton animationDelegate:(nullable id )animationDelegate; + #pragma mark - legacy inits // Legacy init: inits with a label and button, no close button or icon. diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m index 23beeb64..6c1da1fd 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m @@ -60,6 +60,17 @@ return self; } +- (nonnull instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + self.clipsToBounds = YES; + self.height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:96]; + self.height.active = YES; + [self setupViewWithLabelAndImage:nil topImage:nil]; + } + return self; +} + - (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id )animationDelegate { if (self = [self init]) { UIColor *contentColor = topAlertObject.textColor ?: [[MVMCoreUITopAlertView sharedGlobal] getContentColorForType:topAlertObject.type]; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift new file mode 100644 index 00000000..5b4cd3b7 --- /dev/null +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -0,0 +1,252 @@ +// +// MVMCoreUITopAlertView+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/11/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public extension MVMCoreTopAlertObject { + static func create(with model: TopNotificationModel) -> Self { + let object = self.init() + object.persistent = model.persistent + object.type = model.type + object.topAlertDismissTime = model.dismissTime + object.queuePriority = model.priority + object.useNewStyle = true + object.json = model.toJSON() + return object + } +} + +public extension MVMCoreAlertHandler { + + /// Decodes the top alert json to a model. + static func decode(json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) throws -> TopNotificationModel { + let data = try JSONSerialization.data(withJSONObject: json) + let decoder = JSONDecoder() + if let delegateObject = delegateObject { + try decoder.add(delegateObject: delegateObject) + } + return try decoder.decode(TopNotificationModel.self, from: data) + } + + /// Shows the top alert with the json payload. + func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) { + do { + let model = try Self.decode(json: json, delegateObject: delegateObject) + showTopAlert(with: model, delegateObject: delegateObject) + } catch { + if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { + MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + } + } + } + + /// Shows the top alert with the model. + func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) { + let object = MVMCoreTopAlertObject.create(with: model) + guard let pages = model.pages else { + showTopAlert(with: object) + return + } + guard let controller = MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol, + let pageType = controller.pageType, + pages.contains(pageType) else { + showTopAlert(with: object) + // add + return + } + showTopAlert(with: object) + } +} + +protocol StatusBarUI { + func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) +} + +public extension MVMCoreUITopAlertView { + + /// Registers with the notification center to know when json is updated. + @objc func registerWithNotificationCenter() { + NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil) + } + + /// Checks for new top alert json + @objc func responseJSONUpdated(notification: Notification) { + var jssoonnn = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON + /*let molecule = CollapsableNotificationModel(with: LabelModel(text: "Top"), headline: LabelModel(text: "Headline")) + //let molecule = NotificationModel(with: LabelModel(text: "Hello")) + molecule.backgroundColor = Color(uiColor: .mvmRed) + molecule.button = ButtonModel(with: "Hi", action: ActionCancelModel()) + molecule.body = LabelModel(text: "Sup") + molecule.closeButton = NotificationXButtonModel() + let model = TopNotificationModel(with: "Testing", molecule: molecule) + model.persistent = false + model.dismissTime = 10 + model.pages = nil//["settingsLanding"] + jssoonnn?.updateValue(model.toJSON()!, forKey: "TopNotification")*/ + guard let json = jssoonnn?.optionalDictionaryForKey("TopNotification") else { return } + // TODO: Top alert view is current delegate. Should move to current view controller eventually? + let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) + MVMCoreAlertHandler.shared()?.showTopAlert(with: json, delegateObject: delegateObject) + } + + @objc func viewControllerChanged(notification: Notification) { + guard let controller = MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol else { return } + MVMCoreAlertHandler.shared()?.checkPagesDependency(for: controller.pageType) + } + + @objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer?, statusBarStyle: UnsafeMutablePointer?) -> MVMCoreUITopAlertBaseView? { + do { + let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) + guard let json = topAlertObject.json else { return nil } + let model = try MVMCoreAlertHandler.decode(json: json, delegateObject: delegateObject) + guard let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil), + let view = molecule as? MVMCoreUITopAlertBaseView else { + throw ModelRegistry.Error.decoderOther(message: "Molecule not a top alert") + } + if let casteView = view as? StatusBarUI { + let statusBarUI = casteView.getStatusBarUI() + statusBarColor?.pointee = statusBarUI.color + statusBarStyle?.pointee = statusBarUI.style + } + return view + } catch { + if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { + MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + } + return nil + } + } + /* + /// Shows the top alert with the json payload. + func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) { + /*do { + let model = try decode(json: json, delegateObject: delegateObject) + showTopAlert(with: model, delegateObject: delegateObject) + } catch { + if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { + MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + } + }*/ + } + + /// Shows the top alert with the model. + func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) { + if let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil) { + + } + }*/ +} + +public class NotificationXButtonModel: Codable { + public var color: Color? + public var action: ActionModelProtocol? + + private enum CodingKeys: String, CodingKey { + case color + case action + } + + public init() { + } + + public required init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(color, forKey: .color) + try container.encodeModelIfPresent(action, forKey: .action) + } +} + +public class NotificationModel: MoleculeModelProtocol { + public static var identifier: String = "notification" + public var moleculeName: String = NotificationModel.identifier + public var backgroundColor: Color? + public var headline: LabelModel + public var body: LabelModel? + public var button: ButtonModel? + public var closeButton: NotificationXButtonModel? + + init(with headline: LabelModel) { + self.headline = headline + } +} + +extension MVMCoreUITopAlertMainView: MoleculeViewProtocol { + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + 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) + } + 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) + } +} + +public class CollapsableNotificationModel: MoleculeModelProtocol { + public static var identifier: String = "collapsableNotification" + public var moleculeName: String = CollapsableNotificationModel.identifier + public var backgroundColor: Color? + public var topLabel: LabelModel + //public var topAction: ActionModelProtocol? + public var headline: LabelModel + public var body: LabelModel? + public var button: ButtonModel? + public var closeButton: NotificationXButtonModel? + public var alwaysShowCollapsedLabel = false + public var collapseTime: Int = 5 + public var initiallyCollapsed = false + //public var pages: [NSString]? + + init(with topLabel: LabelModel, headline: LabelModel) { + self.topLabel = topLabel + self.headline = headline + } +} + +extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol { + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + guard let model = model as? CollapsableNotificationModel else { return } + backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen + collapseTime = model.collapseTime + onlyShowTopMessageWhenCollapsed = !model.alwaysShowCollapsedLabel + + var actionMap = model.button?.action.toJSON() + if let title = model.button?.title { + actionMap?.updateValue(title, forKey: KeyTitle) + } + buttonView?.setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate) + setTopMessage(model.topLabel.text, message: model.headline.text, subMessage: model.body?.text, contentColor: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil) + expand(false) + //[MVMCoreUITopAlertBaseView addActionToButton:survivalMode.shortView.button actionMap:topAlertObject.buttonMap additionalData:topAlertObject.additionalData]; + // survivalMode.shortView.label.accessibilityTraits = UIAccessibilityTraitButton; + } +} + +extension MVMCoreUITopAlertExpandableView: StatusBarUI { + func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) { + if shortView?.label?.text?.count ?? 0 > 0 { + let color = backgroundColor ?? UIColor.mvmGreen + var greyScale: CGFloat = 0 + if shortView?.label?.textColor.getWhite(&greyScale, alpha: nil) ?? false { + return (color, greyScale > 0.5 ? .lightContent : .default) + } else { + return (color, .default) + } + } else { + return (.white, .default) + } + } +} diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 7efa15a3..903a24d3 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -16,6 +16,7 @@ #import "NSLayoutConstraint+MFConvenience.h" #import "MVMCoreUISession.h" #import "MVMCoreUIUtility.h" +#import @import MVMCore.MVMCoreTopAlertObject; @import MVMCore.MVMCoreLoadHandler; @import MVMCore.MVMCoreNavigationHandler; @@ -105,6 +106,8 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; self.statusBarView = statusBarView; [self setStatusBarColor:[UIColor whiteColor] statusBarStyle:UIStatusBarStyleDefault]; + + [self registerWithNotificationCenter]; } } @@ -122,18 +125,22 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; } - (nonnull MVMCoreUITopAlertBaseView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle { - MVMCoreUITopAlertExpandableView *view = [[MVMCoreUITopAlertExpandableView alloc] initWithTopAlertObject:topAlertObject animationDelegate:animationDelegate viewToLayout:self.superview]; - if (statusBarColor && view.shortView.label.text) { - *statusBarColor = view.backgroundColor; - - if (statusBarStyle) { - CGFloat greyScale = 0; - if ([view.shortView.label.textColor getWhite:&greyScale alpha:nil]) { - *statusBarStyle = greyScale > 0.5 ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; + if (topAlertObject.json) { + return [self moleculeFor:topAlertObject statusBarColor:statusBarColor statusBarStyle:statusBarStyle]; + } else { + MVMCoreUITopAlertExpandableView *view = [[MVMCoreUITopAlertExpandableView alloc] initWithTopAlertObject:topAlertObject animationDelegate:animationDelegate viewToLayout:self.superview]; + if (statusBarColor && view.shortView.label.text) { + *statusBarColor = view.backgroundColor; + + if (statusBarStyle) { + CGFloat greyScale = 0; + if ([view.shortView.label.textColor getWhite:&greyScale alpha:nil]) { + *statusBarStyle = greyScale > 0.5 ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; + } } } + return view; } - return view; } - (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type { From 34256698c07e1c75e212b6581adad44e616e6e84 Mon Sep 17 00:00:00 2001 From: Damodaram Date: Tue, 15 Sep 2020 16:39:41 +0530 Subject: [PATCH 12/54] added action support for selectors --- .../Atomic/Atoms/Selectors/Checkbox.swift | 22 ++++++++++++++----- .../Atoms/Selectors/CheckboxModel.swift | 4 ++++ .../Atomic/Atoms/Selectors/RadioBox.swift | 8 ++++++- .../Atoms/Selectors/RadioBoxModel.swift | 4 ++++ .../Atomic/Atoms/Selectors/RadioSwatch.swift | 8 ++++++- .../Atoms/Selectors/RadioSwatchModel.swift | 4 ++++ 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index f23fd337..fc6607b1 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -381,7 +381,7 @@ import MVMCore checkWidth = 2 checkAndBypassAnimations(selected: false) } - + public override func updateView(_ size: CGFloat) { super.updateView(size) @@ -391,6 +391,12 @@ import MVMCore } } + private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if let actionMap = actionModel.toJSON() { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject @@ -422,10 +428,16 @@ import MVMCore isEnabled = model.enabled - if let action = model.action { - actionBlock = { - if let actionMap = action.toJSON() { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + if (model.action != nil || model.offAction != nil) { + actionBlock = { [weak self] in + guard let self = self else { return } + + if let offAction = model.offAction, !self.isSelected { + self.performCheckboxAction(with: offAction, delegateObject: delegateObject, additionalData: additionalData) + return + } + if let action = model.action { + self.performCheckboxAction(with: action, delegateObject: delegateObject, additionalData: additionalData) } } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 43a08aa6..bb21da1b 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -32,6 +32,7 @@ import Foundation public var invertedColor: Color = Color(uiColor: .mvmWhite) public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack) public var action: ActionModelProtocol? + public var offAction: ActionModelProtocol? public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName @@ -61,6 +62,7 @@ import Foundation case action case fieldKey case groupName + case offAction } //-------------------------------------------------- @@ -155,6 +157,7 @@ import Foundation if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName } + offAction = try typeContainer.decodeModelIfPresent(codingKey: .offAction) } public func encode(to encoder: Encoder) throws { @@ -179,5 +182,6 @@ import Foundation try container.encodeIfPresent(enabled, forKey: .enabled) try container.encodeModelIfPresent(action, forKey: .action) try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encodeModelIfPresent(offAction, forKey: .offAction) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index 70c1a6ef..7b8b5703 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -8,7 +8,7 @@ import Foundation -open class RadioBox: Control { +open class RadioBox: Control, MFButtonProtocol { public let label = Label(fontStyle: .RegularBodySmall) public let subTextLabel = Label(fontStyle: .RegularMicro) public var isOutOfStock = false @@ -22,6 +22,8 @@ open class RadioBox: Control { public var subTextLabelHeightConstraint: NSLayoutConstraint? + private var delegateObject: MVMCoreUIDelegateObject? + public var radioBoxModel: RadioBoxModel? { return model as? RadioBoxModel } @@ -77,6 +79,7 @@ open class RadioBox: Control { 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 } + self.delegateObject = delegateObject label.text = model.text subTextLabel.text = model.subText isOutOfStock = model.strikethrough @@ -135,6 +138,9 @@ open class RadioBox: Control { guard isEnabled else { return } isSelected = true radioBoxModel?.selected = isSelected + if let actionModel = radioBoxModel?.action { + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift index 986eefac..923a65e0 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift @@ -17,6 +17,7 @@ import Foundation public var enabled: Bool = true public var strikethrough: Bool = false public var fieldValue: String? + public var action: ActionModelProtocol? private enum CodingKeys: String, CodingKey { case moleculeName @@ -28,6 +29,7 @@ import Foundation case enabled case strikethrough case fieldValue + case action } required public init(from decoder: Decoder) throws { @@ -47,6 +49,7 @@ import Foundation } fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public func encode(to encoder: Encoder) throws { @@ -60,5 +63,6 @@ import Foundation try container.encode(enabled, forKey: .enabled) try container.encode(strikethrough, forKey: .strikethrough) try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + try container.encodeModelIfPresent(action, forKey: .action) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index 36dcde69..3184feb4 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -9,7 +9,7 @@ import UIKit -open class RadioSwatch: Control { +open class RadioSwatch: Control, MFButtonProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -20,6 +20,8 @@ open class RadioSwatch: Control { private var strikeLayer: CALayer? private var maskLayer: CALayer? + private var delegateObject: MVMCoreUIDelegateObject? + public var radioSwatchModel: RadioSwatchModel? { return model as? RadioSwatchModel } @@ -57,6 +59,7 @@ open class RadioSwatch: Control { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? RadioSwatchModel else { return } + self.delegateObject = delegateObject bottomText.text = model.text isSelected = model.selected isEnabled = model.enabled @@ -117,6 +120,9 @@ open class RadioSwatch: Control { guard isEnabled else { return } isSelected = true radioSwatchModel?.selected = isSelected + if let actionModel = radioSwatchModel?.action { + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift index 3aadd467..7acb3999 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift @@ -17,6 +17,7 @@ import Foundation public var enabled: Bool = true public var strikethrough: Bool = false public var fieldValue: String? + public var action: ActionModelProtocol? private enum CodingKeys: String, CodingKey { case moleculeName @@ -27,6 +28,7 @@ import Foundation case enabled case strikethrough case fieldValue + case action } required public init(from decoder: Decoder) throws { @@ -46,6 +48,7 @@ import Foundation self.strikethrough = strikethrough } fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public func encode(to encoder: Encoder) throws { @@ -58,6 +61,7 @@ import Foundation try container.encode(enabled, forKey: .enabled) try container.encode(strikethrough, forKey: .strikethrough) try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + try container.encodeModelIfPresent(action, forKey: .action) } } From 450b4e3e3dfd77d5d50f7c922f976e2c14539de8 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 15 Sep 2020 11:31:10 -0400 Subject: [PATCH 13/54] Comments, organizing, init fix --- MVMCoreUI.xcodeproj/project.pbxproj | 38 +++- .../CollapsableNotificationModel.swift | 82 +++++++ ...reUITopAlertExpandableView+Extension.swift | 50 +++++ .../MVMCoreUITopAlertMainView+Extension.swift | 23 ++ .../NotificationXButtonModel.swift | 34 +++ .../MVMCoreUITopAlertExpandableView.h | 1 + .../MVMCoreUITopAlertExpandableView.m | 29 ++- .../TopAlert/MVMCoreUITopAlertMainView.h | 2 + .../TopAlert/MVMCoreUITopAlertMainView.m | 22 +- .../MVMCoreUITopAlertView+Extension.swift | 208 ++---------------- .../{Atomic => }/TopNotificationModel.swift | 58 ++++- 11 files changed, 321 insertions(+), 226 deletions(-) create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButtonModel.swift rename MVMCoreUI/TopAlert/{Atomic => }/TopNotificationModel.swift (60%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index f9a0cdb3..b67f6235 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -459,6 +459,11 @@ D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; D2C521A923EDE79E00CA2634 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C521A823EDE79E00CA2634 /* ViewController.swift */; }; D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */; }; + D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CA251104E100C75681 /* NotificationXButtonModel.swift */; }; + D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CC251104FE00C75681 /* NotificationModel.swift */; }; + D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */; }; + D2CAC7D12511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */; }; + D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */; }; D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; }; D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; }; D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; }; @@ -944,6 +949,11 @@ D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; D2C521A823EDE79E00CA2634 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionOpenPanelModel.swift; sourceTree = ""; }; + D2CAC7CA251104E100C75681 /* NotificationXButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButtonModel.swift; sourceTree = ""; }; + D2CAC7CC251104FE00C75681 /* NotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationModel.swift; sourceTree = ""; }; + D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationModel.swift; sourceTree = ""; }; + D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertMainView+Extension.swift"; sourceTree = ""; }; + D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertExpandableView+Extension.swift"; sourceTree = ""; }; D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = ""; }; D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = ""; }; D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = ""; }; @@ -1262,14 +1272,6 @@ path = FourColumn; sourceTree = ""; }; - D20C7007250BF9440095B21C /* Atomic */ = { - isa = PBXGroup; - children = ( - D20C7008250BF99B0095B21C /* TopNotificationModel.swift */, - ); - path = Atomic; - sourceTree = ""; - }; D20FFFB42451E32100A31DA2 /* Device */ = { isa = PBXGroup; children = ( @@ -1705,6 +1707,7 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( + D2CAC7C9251104CB00C75681 /* TopNotification */, D2509ED42472EE0B001BFB9D /* NavigationBar */, D253BB9A24587023002DE544 /* OtherContainers */, D22B38E923F4E07800490EF6 /* DesignedComponents */, @@ -1769,7 +1772,7 @@ D29DF11E21E6851E003B2FB9 /* TopAlert */ = { isa = PBXGroup; children = ( - D20C7007250BF9440095B21C /* Atomic */, + D20C7008250BF99B0095B21C /* TopNotificationModel.swift */, D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */, D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */, D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */, @@ -2070,6 +2073,18 @@ path = Protocols; sourceTree = ""; }; + D2CAC7C9251104CB00C75681 /* TopNotification */ = { + isa = PBXGroup; + children = ( + D2CAC7CA251104E100C75681 /* NotificationXButtonModel.swift */, + D2CAC7CC251104FE00C75681 /* NotificationModel.swift */, + D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */, + D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */, + D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */, + ); + path = TopNotification; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2205,6 +2220,7 @@ D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */, 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */, + D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */, DBC4391822442197001AB423 /* CaretView.swift in Sources */, C07065C42395677300FBF997 /* Link.swift in Sources */, 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */, @@ -2289,6 +2305,7 @@ D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */, AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */, BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */, + D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */, D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */, D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */, D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */, @@ -2315,6 +2332,7 @@ D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */, 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */, 94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */, + D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */, 014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */, AAC23FAF24D92A1E009208DF /* ListThreeColumnSpeedTest.swift in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, @@ -2370,6 +2388,7 @@ AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */, 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */, + D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */, D20923592450ECE00044AD09 /* TableView.swift in Sources */, BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */, @@ -2530,6 +2549,7 @@ AA45AA0D24BF0276007A6EA7 /* LockUpsPlanNames.swift in Sources */, 8DE5BECF2456F7B100772E76 /* ListTwoColumnDropdownSelectors.swift in Sources */, D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, + D2CAC7D12511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift in Sources */, 0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */, 0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */, D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift new file mode 100644 index 00000000..09860232 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift @@ -0,0 +1,82 @@ +// +// CollapsableNotificationModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class CollapsableNotificationModel: MoleculeModelProtocol { + public static var identifier: String = "collapsableNotification" + public var moleculeName: String = CollapsableNotificationModel.identifier + public var backgroundColor: Color? + public var topLabel: LabelModel + public var topAction: ActionModelProtocol? + public var headline: LabelModel + public var body: LabelModel? + public var button: ButtonModel? + public var closeButton: NotificationXButtonModel? + public var alwaysShowCollapsedLabel = false + public var collapseTime: Int = 5 + public var initiallyCollapsed = false + public var pages: [String]? + + init(with topLabel: LabelModel, headline: LabelModel) { + self.topLabel = topLabel + self.headline = headline + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case topLabel + case topAction + case headline + case body + case button + case closeButton + case alwaysShowCollapsedLabel + case collapseTime + case initiallyCollapsed + case pages + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + topLabel = try typeContainer.decode(LabelModel.self, forKey: .topLabel) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + topAction = try typeContainer.decodeModelIfPresent(codingKey: .topAction) + headline = try typeContainer.decode(LabelModel.self, forKey: .headline) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) + button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) + closeButton = try typeContainer.decodeIfPresent(NotificationXButtonModel.self, forKey: .closeButton) + if let alwaysShowCollapsedLabel = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowCollapsedLabel) { + self.alwaysShowCollapsedLabel = alwaysShowCollapsedLabel + } + if let collapseTime = try typeContainer.decodeIfPresent(Int.self, forKey: .collapseTime) { + self.collapseTime = collapseTime + } + if let initiallyCollapsed = try typeContainer.decodeIfPresent(Bool.self, forKey: .initiallyCollapsed) { + self.initiallyCollapsed = initiallyCollapsed + } + pages = try typeContainer.decodeIfPresent([String].self, forKey: .pages) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(topLabel, forKey: .topLabel) + try container.encodeModelIfPresent(topAction, forKey: .topAction) + try container.encode(headline, forKey: .headline) + try container.encodeIfPresent(body, forKey: .body) + try container.encodeIfPresent(button, forKey: .button) + try container.encodeIfPresent(closeButton, forKey: .closeButton) + try container.encode(alwaysShowCollapsedLabel, forKey: .alwaysShowCollapsedLabel) + try container.encode(collapseTime, forKey: .collapseTime) + try container.encode(initiallyCollapsed, forKey: .initiallyCollapsed) + try container.encodeIfPresent(pages, forKey: .pages) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift new file mode 100644 index 00000000..793edf97 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift @@ -0,0 +1,50 @@ +// +// MVMCoreUITopAlertExpandableView+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol { + + 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 + collapseTime = model.collapseTime + onlyShowTopMessageWhenCollapsed = !model.alwaysShowCollapsedLabel + + var actionMap = model.button?.action.toJSON() + if let title = model.button?.title { + actionMap?.updateValue(title, forKey: KeyTitle) + } + buttonView?.setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate) + setTopMessage(model.topLabel.text, message: model.headline.text, subMessage: model.body?.text, contentColor: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil) + expand(false) + + if let button = shortView?.button, + let topActionMap = model.topAction?.toJSON() { + MVMCoreUITopAlertBaseView.addAction(to: button, actionMap: topActionMap, additionalData: nil) + shortView?.label?.accessibilityTraits = .button + } + } +} + +extension MVMCoreUITopAlertExpandableView: StatusBarUI { + func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) { + if shortView?.label?.text?.count ?? 0 > 0 { + let color = backgroundColor ?? UIColor.mvmGreen + var greyScale: CGFloat = 0 + if shortView?.label?.textColor.getWhite(&greyScale, alpha: nil) ?? false { + return (color, greyScale > 0.5 ? .lightContent : .default) + } else { + return (color, .default) + } + } else { + return (.white, .default) + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift new file mode 100644 index 00000000..157d6522 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertMainView+Extension.swift @@ -0,0 +1,23 @@ +// +// MVMCoreUITopAlertMainView+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +extension MVMCoreUITopAlertMainView: MoleculeViewProtocol { + 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) + } + 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/NotificationXButtonModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButtonModel.swift new file mode 100644 index 00000000..368ec1cf --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButtonModel.swift @@ -0,0 +1,34 @@ +// +// NotificationXButtonModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class NotificationXButtonModel: Codable { + public var color: Color? + public var action: ActionModelProtocol? + + private enum CodingKeys: String, CodingKey { + case color + case action + } + + public init() { + } + + public required init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(color, forKey: .color) + try container.encodeModelIfPresent(action, forKey: .action) + } +} diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.h b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.h index 302474cf..04d41363 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.h +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.h @@ -39,6 +39,7 @@ - (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData animationDelegate:(nullable id )animationDelegate viewToLayout:(nonnull UIView *)viewTolayout; // Convenience change functions +- (void)defaultSetup; - (void)setTopMessage:(nullable NSString *)topMessage; - (void)setTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; - (void)setTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m index 7f03fe25..e0eef9d6 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m @@ -76,21 +76,6 @@ return self; } -- (nonnull instancetype)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { - self.translatesAutoresizingMaskIntoConstraints = NO; - self.clipsToBounds = YES; - _collapseAutomaticallyAfterExpanded = YES; - self.viewToLayout = MVMCoreUITopAlertView.sharedGlobal.superview; - self.expanded = NO; - [self setupTopMessage:nil]; - MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] initWithFrame:frame]; - [self setupTopAlertWithButton:topAlertWithButton]; - } - return self; -} - - - (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id )animationDelegate viewToLayout:(nonnull UIView *)viewTolayout { if (self = [self init]) { self.animationDelegate = animationDelegate; @@ -158,6 +143,20 @@ return self; } +- (void)defaultSetup { + if (!self.shortView) { + self.translatesAutoresizingMaskIntoConstraints = NO; + self.clipsToBounds = YES; + _collapseAutomaticallyAfterExpanded = YES; + self.viewToLayout = MVMCoreUITopAlertView.sharedGlobal.superview; + self.expanded = NO; + [self setupTopMessage:nil]; + MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] init]; + [topAlertWithButton defaultSetup]; + [self setupTopAlertWithButton:topAlertWithButton]; + } +} + - (void)setupTopMessage:(nullable NSString *)topMessage { MVMCoreUITopAlertShortView *shortView = [[MVMCoreUITopAlertShortView alloc] initWithColor:[UIColor clearColor] message:nil actionMap:nil additionalData:nil topAlertObject:nil]; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h index 137ac168..b08ca00b 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.h @@ -28,6 +28,8 @@ - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData closeButton:(BOOL)closeButton animationDelegate:(nullable id )animationDelegate; - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage closeButton:(BOOL)closeButton animationDelegate:(nullable id )animationDelegate; +// Sets up without image +- (void)defaultSetup; // Setters for label and button. - (void)setupWithMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m index 6c1da1fd..c817059d 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m @@ -60,17 +60,6 @@ return self; } -- (nonnull instancetype)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { - self.translatesAutoresizingMaskIntoConstraints = NO; - self.clipsToBounds = YES; - self.height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:96]; - self.height.active = YES; - [self setupViewWithLabelAndImage:nil topImage:nil]; - } - return self; -} - - (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id )animationDelegate { if (self = [self init]) { UIColor *contentColor = topAlertObject.textColor ?: [[MVMCoreUITopAlertView sharedGlobal] getContentColorForType:topAlertObject.type]; @@ -121,6 +110,17 @@ } #pragma mark - setup + +- (void)defaultSetup { + if (!self.label) { + self.translatesAutoresizingMaskIntoConstraints = NO; + self.clipsToBounds = YES; + self.height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:96]; + self.height.active = YES; + [self setupViewWithLabelAndImage:nil topImage:nil]; + } +} + - (void)setupViewWithLabelAndImage:(NSString *)imageURL topImage:(NSString *)topImageString { UIView *centerView = [MVMCoreUICommonViewsUtility commonView]; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 5b4cd3b7..99573688 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -8,61 +8,7 @@ import Foundation -public extension MVMCoreTopAlertObject { - static func create(with model: TopNotificationModel) -> Self { - let object = self.init() - object.persistent = model.persistent - object.type = model.type - object.topAlertDismissTime = model.dismissTime - object.queuePriority = model.priority - object.useNewStyle = true - object.json = model.toJSON() - return object - } -} - -public extension MVMCoreAlertHandler { - - /// Decodes the top alert json to a model. - static func decode(json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) throws -> TopNotificationModel { - let data = try JSONSerialization.data(withJSONObject: json) - let decoder = JSONDecoder() - if let delegateObject = delegateObject { - try decoder.add(delegateObject: delegateObject) - } - return try decoder.decode(TopNotificationModel.self, from: data) - } - - /// Shows the top alert with the json payload. - func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) { - do { - let model = try Self.decode(json: json, delegateObject: delegateObject) - showTopAlert(with: model, delegateObject: delegateObject) - } catch { - if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { - MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) - } - } - } - - /// Shows the top alert with the model. - func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) { - let object = MVMCoreTopAlertObject.create(with: model) - guard let pages = model.pages else { - showTopAlert(with: object) - return - } - guard let controller = MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol, - let pageType = controller.pageType, - pages.contains(pageType) else { - showTopAlert(with: object) - // add - return - } - showTopAlert(with: object) - } -} - +/// Allows top alerts to determine the status bar color and style. protocol StatusBarUI { func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) } @@ -87,24 +33,45 @@ public extension MVMCoreUITopAlertView { let model = TopNotificationModel(with: "Testing", molecule: molecule) model.persistent = false model.dismissTime = 10 + model.setPriority(with: 80) model.pages = nil//["settingsLanding"] jssoonnn?.updateValue(model.toJSON()!, forKey: "TopNotification")*/ guard let json = jssoonnn?.optionalDictionaryForKey("TopNotification") else { return } // TODO: Top alert view is current delegate. Should move to current view controller eventually? let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) - MVMCoreAlertHandler.shared()?.showTopAlert(with: json, delegateObject: delegateObject) + showTopAlert(with: json, delegateObject: delegateObject) } + /// When a detail page changes, check top alerts. @objc func viewControllerChanged(notification: Notification) { guard let controller = MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol else { return } MVMCoreAlertHandler.shared()?.checkPagesDependency(for: controller.pageType) } + /// Shows the top alert with the json payload. + func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) { + do { + let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject) + showTopAlert(with: model, delegateObject: delegateObject) + } catch { + if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { + MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + } + } + } + + /// Shows the top alert with the model. + func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) { + let object = model.createTopAlertObject() + MVMCoreAlertHandler.shared()?.showTopAlert(with: object) + } + + /// Returns the top alert molecule to use and status bar color legacy style. @objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer?, statusBarStyle: UnsafeMutablePointer?) -> MVMCoreUITopAlertBaseView? { do { let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) guard let json = topAlertObject.json else { return nil } - let model = try MVMCoreAlertHandler.decode(json: json, delegateObject: delegateObject) + let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject) guard let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil), let view = molecule as? MVMCoreUITopAlertBaseView else { throw ModelRegistry.Error.decoderOther(message: "Molecule not a top alert") @@ -122,131 +89,4 @@ public extension MVMCoreUITopAlertView { return nil } } - /* - /// Shows the top alert with the json payload. - func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) { - /*do { - let model = try decode(json: json, delegateObject: delegateObject) - showTopAlert(with: model, delegateObject: delegateObject) - } catch { - if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { - MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) - } - }*/ - } - - /// Shows the top alert with the model. - func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) { - if let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil) { - - } - }*/ -} - -public class NotificationXButtonModel: Codable { - public var color: Color? - public var action: ActionModelProtocol? - - private enum CodingKeys: String, CodingKey { - case color - case action - } - - public init() { - } - - public required init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) - action = try typeContainer.decodeModelIfPresent(codingKey: .action) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeIfPresent(color, forKey: .color) - try container.encodeModelIfPresent(action, forKey: .action) - } -} - -public class NotificationModel: MoleculeModelProtocol { - public static var identifier: String = "notification" - public var moleculeName: String = NotificationModel.identifier - public var backgroundColor: Color? - public var headline: LabelModel - public var body: LabelModel? - public var button: ButtonModel? - public var closeButton: NotificationXButtonModel? - - init(with headline: LabelModel) { - self.headline = headline - } -} - -extension MVMCoreUITopAlertMainView: MoleculeViewProtocol { - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - 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) - } - 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) - } -} - -public class CollapsableNotificationModel: MoleculeModelProtocol { - public static var identifier: String = "collapsableNotification" - public var moleculeName: String = CollapsableNotificationModel.identifier - public var backgroundColor: Color? - public var topLabel: LabelModel - //public var topAction: ActionModelProtocol? - public var headline: LabelModel - public var body: LabelModel? - public var button: ButtonModel? - public var closeButton: NotificationXButtonModel? - public var alwaysShowCollapsedLabel = false - public var collapseTime: Int = 5 - public var initiallyCollapsed = false - //public var pages: [NSString]? - - init(with topLabel: LabelModel, headline: LabelModel) { - self.topLabel = topLabel - self.headline = headline - } -} - -extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol { - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - guard let model = model as? CollapsableNotificationModel else { return } - backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen - collapseTime = model.collapseTime - onlyShowTopMessageWhenCollapsed = !model.alwaysShowCollapsedLabel - - var actionMap = model.button?.action.toJSON() - if let title = model.button?.title { - actionMap?.updateValue(title, forKey: KeyTitle) - } - buttonView?.setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate) - setTopMessage(model.topLabel.text, message: model.headline.text, subMessage: model.body?.text, contentColor: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil) - expand(false) - //[MVMCoreUITopAlertBaseView addActionToButton:survivalMode.shortView.button actionMap:topAlertObject.buttonMap additionalData:topAlertObject.additionalData]; - // survivalMode.shortView.label.accessibilityTraits = UIAccessibilityTraitButton; - } -} - -extension MVMCoreUITopAlertExpandableView: StatusBarUI { - func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) { - if shortView?.label?.text?.count ?? 0 > 0 { - let color = backgroundColor ?? UIColor.mvmGreen - var greyScale: CGFloat = 0 - if shortView?.label?.textColor.getWhite(&greyScale, alpha: nil) ?? false { - return (color, greyScale > 0.5 ? .lightContent : .default) - } else { - return (color, .default) - } - } else { - return (.white, .default) - } - } } diff --git a/MVMCoreUI/TopAlert/Atomic/TopNotificationModel.swift b/MVMCoreUI/TopAlert/TopNotificationModel.swift similarity index 60% rename from MVMCoreUI/TopAlert/Atomic/TopNotificationModel.swift rename to MVMCoreUI/TopAlert/TopNotificationModel.swift index d6900c85..7c07c980 100644 --- a/MVMCoreUI/TopAlert/Atomic/TopNotificationModel.swift +++ b/MVMCoreUI/TopAlert/TopNotificationModel.swift @@ -27,6 +27,55 @@ open class TopNotificationModel: Codable { case analyticsData } + //-------------------------------------------------- + // MARK: - Convenience Functions + //-------------------------------------------------- + + /// Set the priority using percent 0-100 + open func setPriority(with percent: Float) { + // The new scale + let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue + + // Adjust the percent to the new scale + let scaledPercent = (percent / 100.0) * Float(scale) + + // Finish shifting. + priority = Operation.QueuePriority(rawValue: Operation.QueuePriority.veryLow.rawValue + Int(scaledPercent)) ?? .normal + } + + /// Gets the priority as a percent (0-100) + open func getPriorityPercent() -> Float { + // Shift the value + let shifted = Float(priority.rawValue - Operation.QueuePriority.veryLow.rawValue) + + // The current scale + let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue + + // Adjust to percent + return (shifted / Float(scale)) * 100.0 + } + + open func createTopAlertObject() -> MVMCoreTopAlertObject { + let object = MVMCoreTopAlertObject() + object.persistent = persistent + object.type = type + object.topAlertDismissTime = dismissTime + object.queuePriority = priority + object.useNewStyle = true + object.json = toJSON() + return object + } + + /// Decodes the top alert json to a model. + public static func decode(json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) throws -> Self { + let data = try JSONSerialization.data(withJSONObject: json) + let decoder = JSONDecoder() + if let delegateObject = delegateObject { + try decoder.add(delegateObject: delegateObject) + } + return try decoder.decode(self, from: data) + } + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- @@ -50,9 +99,7 @@ open class TopNotificationModel: Codable { type = try typeContainer.decode(String.self, forKey: .type) molecule = try typeContainer.decodeModel(codingKey: .molecule) if let priorityPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .priority) { - let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue - let scaledPercent = (priorityPercent / 100.0) * Float(scale) - self.priority = Operation.QueuePriority(rawValue: Operation.QueuePriority.veryLow.rawValue + Int(scaledPercent)) ?? .normal + setPriority(with: priorityPercent) } if let persistent = try typeContainer.decodeIfPresent(Bool.self, forKey: .persistent) { self.persistent = persistent @@ -68,10 +115,7 @@ open class TopNotificationModel: Codable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(type, forKey: .type) try container.encodeModel(molecule, forKey: .molecule) - - let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue - let priorityPercent = Int((Float(priority.rawValue - Operation.QueuePriority.veryLow.rawValue) / Float(scale)) * 100.0) - try container.encode(priorityPercent, forKey: .priority) + try container.encode(getPriorityPercent(), forKey: .priority) try container.encode(persistent, forKey: .persistent) try container.encode(dismissTime, forKey: .dismissTime) try container.encodeIfPresent(pages, forKey: .pages) From 4e5639407e1aac82da2cf4fa106fc9d2e49413d7 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 15 Sep 2020 11:41:56 -0400 Subject: [PATCH 14/54] comments --- .../TopNotification/NotificationModel.swift | 23 +++++++++++++++++++ .../MVMCoreUITopAlertView+Extension.swift | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift new file mode 100644 index 00000000..ac74abb6 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift @@ -0,0 +1,23 @@ +// +// NotificationModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class NotificationModel: MoleculeModelProtocol { + public static var identifier: String = "notification" + public var moleculeName: String = NotificationModel.identifier + public var backgroundColor: Color? + public var headline: LabelModel + public var body: LabelModel? + public var button: ButtonModel? + public var closeButton: NotificationXButtonModel? + + init(with headline: LabelModel) { + self.headline = headline + } +} diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 99573688..d14d100f 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -37,6 +37,8 @@ public extension MVMCoreUITopAlertView { model.pages = nil//["settingsLanding"] jssoonnn?.updateValue(model.toJSON()!, forKey: "TopNotification")*/ guard let json = jssoonnn?.optionalDictionaryForKey("TopNotification") else { return } + print("TYTYT top alert new json \(json)") + // TODO: Top alert view is current delegate. Should move to current view controller eventually? let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) showTopAlert(with: json, delegateObject: delegateObject) From 83a5010f65a0afcdadce8ce268a7718c322f6dfd Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 15 Sep 2020 12:20:41 -0400 Subject: [PATCH 15/54] remove print statements --- .../MVMCoreUITopAlertView+Extension.swift | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index d14d100f..027875e7 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -23,21 +23,8 @@ public extension MVMCoreUITopAlertView { /// Checks for new top alert json @objc func responseJSONUpdated(notification: Notification) { - var jssoonnn = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON - /*let molecule = CollapsableNotificationModel(with: LabelModel(text: "Top"), headline: LabelModel(text: "Headline")) - //let molecule = NotificationModel(with: LabelModel(text: "Hello")) - molecule.backgroundColor = Color(uiColor: .mvmRed) - molecule.button = ButtonModel(with: "Hi", action: ActionCancelModel()) - molecule.body = LabelModel(text: "Sup") - molecule.closeButton = NotificationXButtonModel() - let model = TopNotificationModel(with: "Testing", molecule: molecule) - model.persistent = false - model.dismissTime = 10 - model.setPriority(with: 80) - model.pages = nil//["settingsLanding"] - jssoonnn?.updateValue(model.toJSON()!, forKey: "TopNotification")*/ - guard let json = jssoonnn?.optionalDictionaryForKey("TopNotification") else { return } - print("TYTYT top alert new json \(json)") + let responseJSON = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON + guard let json = responseJSON?.optionalDictionaryForKey("TopNotification") else { return } // TODO: Top alert view is current delegate. Should move to current view controller eventually? let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) From 5407ec9cefad806252835db49bf5286e7fe1ae2b Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 15 Sep 2020 14:10:45 -0400 Subject: [PATCH 16/54] tab bar history --- .../HorizontalCombinationViews/TabBar.swift | 4 +++ .../Atomic/Protocols/TabBarProtocol.swift | 3 ++ .../BaseControllers/ViewController.swift | 34 ++++++++++++------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift index 1cf06325..1ee7ee71 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift @@ -99,6 +99,10 @@ import Foundation self.tabBar(self, didSelect: newSelectedItem) }) } + + public func currentTabIndex() -> Int { + return model.selectedTab + } } extension UITabBarItem: MFButtonProtocol { diff --git a/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift b/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift index 19c7e107..1ee147f7 100644 --- a/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift @@ -17,4 +17,7 @@ import Foundation /// Should select the tab index. As if the user selected it. @objc func selectTab(at index: Int) + + /// Returns the current tab + @objc func currentTabIndex() -> Int } diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 336d5da6..f0d6ab84 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -39,6 +39,9 @@ import UIKit public var selectedField: UIView? + // Stores the previous tab bar index. + public var tabBarIndex: Int? + /// Checks if the screen width has changed open func screenSizeChanged() -> Bool { return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) @@ -318,13 +321,27 @@ import UIKit //-------------------------------------------------- open func updateTabBar() { - guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self, - let tabModel = pageModel as? TabPageModelProtocol else { return } + guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self else { return } MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar - if let index = tabModel.tabBarIndex { + + if let index = (pageModel as? TabPageModelProtocol)?.tabBarIndex { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) + } else if let index = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int { + MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) + } else if let index = self.tabBarIndex { + MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) + } else if let index = MVMCoreUISplitViewController.main()?.tabBar?.currentTabIndex() { + // Store current tab index for cases like back button. + self.tabBarIndex = index + } + + if let hidden = (pageModel as? TabPageModelProtocol)?.tabBarHidden { + MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) + } else if let hidden = loadObject?.requestParameters?.actionMap?["tabBarHidden"] as? Bool { + MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) + } else { + MVMCoreUISplitViewController.main()?.updateTabBarShowing(true) } - MVMCoreUISplitViewController.main()?.updateTabBarShowing(!tabModel.tabBarHidden) } //-------------------------------------------------- @@ -454,15 +471,6 @@ import UIKit loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true { MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true) } - - // Selects the tab if needed. Page driven takes priority over action driven (see viewWillAppear) - if let tab: Int = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int, - error == nil, - loadObject?.pageJSON?["tabBarIndex"] == nil { - MVMCoreDispatchUtility.performBlock(onMainThread: { - MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: tab) - }) - } } //-------------------------------------------------- From 80d15f84da99fd4bb414e0cbe3f1410c7ca84399 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 15 Sep 2020 15:15:10 -0400 Subject: [PATCH 17/54] review updates --- .../TopAlert/MVMCoreUITopAlertView+Extension.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 027875e7..e6df7178 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -23,8 +23,8 @@ public extension MVMCoreUITopAlertView { /// Checks for new top alert json @objc func responseJSONUpdated(notification: Notification) { - let responseJSON = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON - guard let json = responseJSON?.optionalDictionaryForKey("TopNotification") else { return } + guard let responseJSON = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON, + let json = responseJSON.optionalDictionaryForKey("TopNotification") else { return } // TODO: Top alert view is current delegate. Should move to current view controller eventually? let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) @@ -65,10 +65,10 @@ public extension MVMCoreUITopAlertView { let view = molecule as? MVMCoreUITopAlertBaseView else { throw ModelRegistry.Error.decoderOther(message: "Molecule not a top alert") } - if let casteView = view as? StatusBarUI { - let statusBarUI = casteView.getStatusBarUI() - statusBarColor?.pointee = statusBarUI.color - statusBarStyle?.pointee = statusBarUI.style + if let castView = view as? StatusBarUI { + let (color, style) = castView.getStatusBarUI() + statusBarColor?.pointee = color + statusBarStyle?.pointee = style } return view } catch { From 483a50dd98383744650eb1fac6d202d7fb732ff0 Mon Sep 17 00:00:00 2001 From: Damodaram Date: Wed, 16 Sep 2020 15:45:56 +0530 Subject: [PATCH 18/54] tagging changes implemented --- .../TextFields/BaseDropdownEntryField.swift | 23 +++++++++++++++++++ .../BaseDropdownEntryFieldModel.swift | 4 ++++ .../TextFields/DateDropdownEntryField.swift | 1 + .../TextFields/ItemDropdownEntryField.swift | 1 + MVMCoreUI/Utility/MVMCoreUIConstants.h | 2 ++ MVMCoreUI/Utility/MVMCoreUIConstants.m | 2 ++ 6 files changed, 33 insertions(+) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index ee697a3e..815fe2d2 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -27,6 +27,10 @@ import UIKit return caret }() + public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? { + return model as? BaseDropdownEntryFieldModel + } + //-------------------------------------------------- // MARK: - Property Observers //-------------------------------------------------- @@ -39,6 +43,14 @@ import UIKit } } + public var isTextChanged: Bool = false { + didSet { + if(isTextChanged) { + performDropdownAction() + } + } + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -80,4 +92,15 @@ import UIKit dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) } + + private func performDropdownAction() { + if let actionModel = baseDropdownEntryFieldModel?.action, var actionMap = actionModel.toJSON() { + if var analyticsData = actionMap[KeyAnalyticsData] as? JSONDictionary { + let taggedValue = (analyticsData[KeyAdobeTrackerLinkName] as? String ?? "") + (text ?? "") + analyticsData[KeyAdobeTrackerLinkName] = taggedValue + actionMap[KeyAnalyticsData] = analyticsData + } + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: delegateObject) + } + } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift index e34183de..0bb17284 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift @@ -12,6 +12,7 @@ //-------------------------------------------------- public var caretView: CaretViewModel? + public var action: ActionModelProtocol? public override class var identifier: String { return "" @@ -24,6 +25,7 @@ private enum CodingKeys: String, CodingKey { case moleculeName case caretView + case action } //-------------------------------------------------- @@ -34,6 +36,7 @@ try super.init(from: decoder) let typeContainer = try decoder.container(keyedBy: CodingKeys.self) caretView = try typeContainer.decodeIfPresent(CaretViewModel.self, forKey: .caretView) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public override func encode(to encoder: Encoder) throws { @@ -41,5 +44,6 @@ var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(caretView, forKey: .caretView) + try container.encodeModelIfPresent(action, forKey: .action) } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift index 6ff950f6..edf1423f 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift @@ -101,6 +101,7 @@ import UIKit } else { text = dateDropdownModel?.dateFormatter.string(from: date) } + isTextChanged = true } @objc public override func dismissFieldInput(_ sender: Any?) { diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift index cc8a4c71..735628bb 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift @@ -138,6 +138,7 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { observeDropdownChange?(text ?? "", pickerData[row]) text = pickerData[row] itemDropdownEntryFieldModel?.selectedIndex = row + isTextChanged = true } } diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.h b/MVMCoreUI/Utility/MVMCoreUIConstants.h index 4c0a3553..a76bbc97 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.h +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.h @@ -42,6 +42,8 @@ extern NSString * const KeyIsOpaque; extern NSString * const KeyFieldKey; extern NSString * const KeyRequired; +extern NSString * const KeyAnalyticsData; +extern NSString * const KeyAdobeTrackerLinkName; #pragma mark - Values diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.m b/MVMCoreUI/Utility/MVMCoreUIConstants.m index 5f5a9a45..3e6ac813 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.m +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.m @@ -40,6 +40,8 @@ NSString * const KeyTextColor = @"textColor"; NSString * const KeyIsHidden = @"isHidden"; NSString * const KeyIsOpaque = @"isOpaque"; +NSString * const KeyAnalyticsData = @"analyticsData"; +NSString * const KeyAdobeTrackerLinkName = @"vzwi.mvmapp.LinkName"; #pragma mark - Values From 88a6421a7ee4e4fb69f9ddfd06f8bb728d8f5b0c Mon Sep 17 00:00:00 2001 From: Damodaram Date: Wed, 16 Sep 2020 20:28:55 +0530 Subject: [PATCH 19/54] updated based on review comments --- .../FormFields/TextFields/BaseDropdownEntryField.swift | 9 ++------- MVMCoreUI/Utility/MVMCoreUIConstants.h | 2 -- MVMCoreUI/Utility/MVMCoreUIConstants.m | 2 -- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index 815fe2d2..f50ee379 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -94,13 +94,8 @@ import UIKit } private func performDropdownAction() { - if let actionModel = baseDropdownEntryFieldModel?.action, var actionMap = actionModel.toJSON() { - if var analyticsData = actionMap[KeyAnalyticsData] as? JSONDictionary { - let taggedValue = (analyticsData[KeyAdobeTrackerLinkName] as? String ?? "") + (text ?? "") - analyticsData[KeyAdobeTrackerLinkName] = taggedValue - actionMap[KeyAnalyticsData] = analyticsData - } - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: delegateObject) + if let actionModel = baseDropdownEntryFieldModel?.action, let actionMap = actionModel.toJSON() { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: baseDropdownEntryFieldModel?.toJSON(), delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.h b/MVMCoreUI/Utility/MVMCoreUIConstants.h index a76bbc97..4c0a3553 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.h +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.h @@ -42,8 +42,6 @@ extern NSString * const KeyIsOpaque; extern NSString * const KeyFieldKey; extern NSString * const KeyRequired; -extern NSString * const KeyAnalyticsData; -extern NSString * const KeyAdobeTrackerLinkName; #pragma mark - Values diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.m b/MVMCoreUI/Utility/MVMCoreUIConstants.m index 3e6ac813..5f5a9a45 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.m +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.m @@ -40,8 +40,6 @@ NSString * const KeyTextColor = @"textColor"; NSString * const KeyIsHidden = @"isHidden"; NSString * const KeyIsOpaque = @"isOpaque"; -NSString * const KeyAnalyticsData = @"analyticsData"; -NSString * const KeyAdobeTrackerLinkName = @"vzwi.mvmapp.LinkName"; #pragma mark - Values From b01a34365bcd350ec6547dd47eeefed18d401812 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 16 Sep 2020 18:42:58 -0400 Subject: [PATCH 20/54] missed name --- .../TopNotification/CollapsableNotificationModel.swift | 10 +++++----- .../MVMCoreUITopAlertExpandableView+Extension.swift | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift index 09860232..15db1009 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift @@ -18,7 +18,7 @@ public class CollapsableNotificationModel: MoleculeModelProtocol { public var body: LabelModel? public var button: ButtonModel? public var closeButton: NotificationXButtonModel? - public var alwaysShowCollapsedLabel = false + public var alwaysShowTopLabel = false public var collapseTime: Int = 5 public var initiallyCollapsed = false public var pages: [String]? @@ -37,7 +37,7 @@ public class CollapsableNotificationModel: MoleculeModelProtocol { case body case button case closeButton - case alwaysShowCollapsedLabel + case alwaysShowTopLabel case collapseTime case initiallyCollapsed case pages @@ -52,8 +52,8 @@ public class CollapsableNotificationModel: MoleculeModelProtocol { body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) closeButton = try typeContainer.decodeIfPresent(NotificationXButtonModel.self, forKey: .closeButton) - if let alwaysShowCollapsedLabel = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowCollapsedLabel) { - self.alwaysShowCollapsedLabel = alwaysShowCollapsedLabel + if let alwaysShowTopLabel = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowTopLabel) { + self.alwaysShowTopLabel = alwaysShowTopLabel } if let collapseTime = try typeContainer.decodeIfPresent(Int.self, forKey: .collapseTime) { self.collapseTime = collapseTime @@ -74,7 +74,7 @@ public class CollapsableNotificationModel: MoleculeModelProtocol { try container.encodeIfPresent(body, forKey: .body) try container.encodeIfPresent(button, forKey: .button) try container.encodeIfPresent(closeButton, forKey: .closeButton) - try container.encode(alwaysShowCollapsedLabel, forKey: .alwaysShowCollapsedLabel) + try container.encode(alwaysShowTopLabel, forKey: .alwaysShowTopLabel) try container.encode(collapseTime, forKey: .collapseTime) try container.encode(initiallyCollapsed, forKey: .initiallyCollapsed) try container.encodeIfPresent(pages, forKey: .pages) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift index 793edf97..e28bf057 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/MVMCoreUITopAlertExpandableView+Extension.swift @@ -15,7 +15,7 @@ extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol { guard let model = model as? CollapsableNotificationModel else { return } backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen collapseTime = model.collapseTime - onlyShowTopMessageWhenCollapsed = !model.alwaysShowCollapsedLabel + onlyShowTopMessageWhenCollapsed = !model.alwaysShowTopLabel var actionMap = model.button?.action.toJSON() if let title = model.button?.title { From 4254e63993aea373ad5e06ad2072f81697126f1e Mon Sep 17 00:00:00 2001 From: Damodaram Date: Thu, 17 Sep 2020 19:52:33 +0530 Subject: [PATCH 21/54] sending model json --- MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 6 +++++- MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift | 2 +- MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift | 2 +- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index fc6607b1..6e52e735 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -24,6 +24,10 @@ import MVMCore var groupName: String? var delegateObject: MVMCoreUIDelegateObject? + public var checkboxModel: CheckboxModel? { + return model as? CheckboxModel + } + public static let defaultHeightWidth: CGFloat = 18.0 /// If true the border of this checkbox will be circular. @@ -393,7 +397,7 @@ import MVMCore private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { if let actionMap = actionModel.toJSON() { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: checkboxModel?.toJSON(), delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index 7b8b5703..793348c9 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -139,7 +139,7 @@ open class RadioBox: Control, MFButtonProtocol { isSelected = true radioBoxModel?.selected = isSelected if let actionModel = radioBoxModel?.action { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: radioBoxModel?.toJSON()) } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index e1f3ab93..30cc4bb8 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -100,7 +100,7 @@ import UIKit isSelected = !isSelected } if let actionModel = radioModel?.action, isSelected { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: radioModel?.toJSON()) } _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) setNeedsDisplay() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index 3184feb4..f921616f 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -121,7 +121,7 @@ open class RadioSwatch: Control, MFButtonProtocol { isSelected = true radioSwatchModel?.selected = isSelected if let actionModel = radioSwatchModel?.action { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: radioSwatchModel?.toJSON()) } layer.setNeedsDisplay() } From 81aa0da7cbb88305d9686b7eecfa4dfac7c05f00 Mon Sep 17 00:00:00 2001 From: Damodaram Date: Fri, 18 Sep 2020 12:34:57 +0530 Subject: [PATCH 22/54] sending source model to action --- .../FormFields/TextFields/BaseDropdownEntryField.swift | 7 +++++-- MVMCoreUI/Utility/MVMCoreUIConstants.h | 1 + MVMCoreUI/Utility/MVMCoreUIConstants.m | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index f50ee379..9a0b6923 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -30,6 +30,7 @@ import UIKit public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? { return model as? BaseDropdownEntryFieldModel } + var additionalData: [AnyHashable: Any]? //-------------------------------------------------- // MARK: - Property Observers @@ -87,7 +88,7 @@ import UIKit public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - + self.additionalData = additionalData guard let model = model as? BaseDropdownEntryFieldModel else { return } dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) @@ -95,7 +96,9 @@ import UIKit private func performDropdownAction() { if let actionModel = baseDropdownEntryFieldModel?.action, let actionMap = actionModel.toJSON() { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: baseDropdownEntryFieldModel?.toJSON(), delegateObject: delegateObject) + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = baseDropdownEntryFieldModel + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.h b/MVMCoreUI/Utility/MVMCoreUIConstants.h index 4c0a3553..684d6044 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.h +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.h @@ -42,6 +42,7 @@ extern NSString * const KeyIsOpaque; extern NSString * const KeyFieldKey; extern NSString * const KeyRequired; +extern NSString * const KeySourceModel; #pragma mark - Values diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.m b/MVMCoreUI/Utility/MVMCoreUIConstants.m index 5f5a9a45..e4954b01 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.m +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.m @@ -40,6 +40,7 @@ NSString * const KeyTextColor = @"textColor"; NSString * const KeyIsHidden = @"isHidden"; NSString * const KeyIsOpaque = @"isOpaque"; +NSString * const KeySourceModel = @"sourceModel"; #pragma mark - Values From b1fb3f075f9d28e6e22b1b496ea30200d9b0f2f8 Mon Sep 17 00:00:00 2001 From: Damodaram Date: Fri, 18 Sep 2020 12:54:31 +0530 Subject: [PATCH 23/54] updated additional data for selectors --- MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 4 +++- MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift | 6 +++++- MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift | 6 +++++- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift | 6 +++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 6e52e735..b6a87cd9 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -397,7 +397,9 @@ import MVMCore private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { if let actionMap = actionModel.toJSON() { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: checkboxModel?.toJSON(), delegateObject: delegateObject) + var additionalDatatoUpdate = additionalData ?? [:] + additionalDatatoUpdate[KeySourceModel] = checkboxModel + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index 793348c9..46b4cef5 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -23,6 +23,7 @@ open class RadioBox: Control, MFButtonProtocol { public var subTextLabelHeightConstraint: NSLayoutConstraint? private var delegateObject: MVMCoreUIDelegateObject? + var additionalData: [AnyHashable: Any]? public var radioBoxModel: RadioBoxModel? { return model as? RadioBoxModel @@ -80,6 +81,7 @@ open class RadioBox: Control, MFButtonProtocol { super.set(with: model, delegateObject, additionalData) guard let model = model as? RadioBoxModel else { return } self.delegateObject = delegateObject + self.additionalData = additionalData label.text = model.text subTextLabel.text = model.subText isOutOfStock = model.strikethrough @@ -139,7 +141,9 @@ open class RadioBox: Control, MFButtonProtocol { isSelected = true radioBoxModel?.selected = isSelected if let actionModel = radioBoxModel?.action { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: radioBoxModel?.toJSON()) + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = radioBoxModel + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index 30cc4bb8..9e18f936 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -30,6 +30,7 @@ import UIKit public var enabledColor: UIColor = .mvmBlack public var disabledColor: UIColor = .mvmCoolGray3 public var delegateObject: MVMCoreUIDelegateObject? + var additionalData: [AnyHashable: Any]? public var radioModel: RadioButtonModel? { return model as? RadioButtonModel @@ -100,7 +101,9 @@ import UIKit isSelected = !isSelected } if let actionModel = radioModel?.action, isSelected { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: radioModel?.toJSON()) + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = radioModel + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) } _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) setNeedsDisplay() @@ -158,6 +161,7 @@ import UIKit public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject + self.additionalData = additionalData guard let model = model as? RadioButtonModel else { return } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index f921616f..e1502515 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -21,6 +21,7 @@ open class RadioSwatch: Control, MFButtonProtocol { private var maskLayer: CALayer? private var delegateObject: MVMCoreUIDelegateObject? + var additionalData: [AnyHashable: Any]? public var radioSwatchModel: RadioSwatchModel? { return model as? RadioSwatchModel @@ -60,6 +61,7 @@ open class RadioSwatch: Control, MFButtonProtocol { super.set(with: model, delegateObject, additionalData) guard let model = model as? RadioSwatchModel else { return } self.delegateObject = delegateObject + self.additionalData = additionalData bottomText.text = model.text isSelected = model.selected isEnabled = model.enabled @@ -121,7 +123,9 @@ open class RadioSwatch: Control, MFButtonProtocol { isSelected = true radioSwatchModel?.selected = isSelected if let actionModel = radioSwatchModel?.action { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: radioSwatchModel?.toJSON()) + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = radioSwatchModel + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) } layer.setNeedsDisplay() } From 44dad679616c479450a9a925cf0587d49c84524e Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 18 Sep 2020 12:39:58 -0400 Subject: [PATCH 24/54] push latest iteration for perform action --- .../FormFields/TextFields/BaseDropdownEntryField.swift | 10 +--------- .../FormFields/TextFields/DateDropdownEntryField.swift | 2 +- .../FormFields/TextFields/ItemDropdownEntryField.swift | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index 9a0b6923..2076d0fc 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -44,14 +44,6 @@ import UIKit } } - public var isTextChanged: Bool = false { - didSet { - if(isTextChanged) { - performDropdownAction() - } - } - } - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -94,7 +86,7 @@ import UIKit dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) } - private func performDropdownAction() { + func performDropdownAction() { if let actionModel = baseDropdownEntryFieldModel?.action, let actionMap = actionModel.toJSON() { var additionalData = self.additionalData ?? [:] additionalData[KeySourceModel] = baseDropdownEntryFieldModel diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift index edf1423f..b8fba094 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift @@ -101,12 +101,12 @@ import UIKit } else { text = dateDropdownModel?.dateFormatter.string(from: date) } - isTextChanged = true } @objc public override func dismissFieldInput(_ sender: Any?) { setTextWith(date: datePicker?.date) + performDropdownAction() super.dismissFieldInput(sender) } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift index 735628bb..ea82a922 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift @@ -138,7 +138,7 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { observeDropdownChange?(text ?? "", pickerData[row]) text = pickerData[row] itemDropdownEntryFieldModel?.selectedIndex = row - isTextChanged = true + performDropdownAction() } } From 68274505fbdce0ab49c91c055823179f9ff02b1b Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 18 Sep 2020 13:08:08 -0400 Subject: [PATCH 25/54] some checks for analytics change --- .../TextFields/ItemDropdownEntryField.swift | 12 +++++++++--- .../TextFields/ItemDropdownEntryFieldModel.swift | 16 +++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift index ea82a922..6c2a0f4e 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift @@ -32,6 +32,8 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { return model as? ItemDropdownEntryFieldModel } + private var systemWillSetItem = false + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -109,8 +111,10 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { pickerData = model.options setPickerDelegates(delegate: self) - if let pickerView = pickerView { - self.pickerView(pickerView, didSelectRow: model.selectedIndex, inComponent: 0) + if let pickerView = pickerView, let index = model.selectedIndex { + systemWillSetItem = true + self.pickerView(pickerView, didSelectRow: index, inComponent: 0) + systemWillSetItem = false } } } @@ -138,7 +142,9 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { observeDropdownChange?(text ?? "", pickerData[row]) text = pickerData[row] itemDropdownEntryFieldModel?.selectedIndex = row - performDropdownAction() + if !systemWillSetItem { + performDropdownAction() + } } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift index d89f5e16..0bc1ee0a 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift @@ -16,11 +16,14 @@ } public var options: [String] = [] - public var selectedIndex: Int = 0 + public var selectedIndex: Int? public override func formFieldValue() -> AnyHashable? { - guard !options.isEmpty else { return nil } - return options[selectedIndex] + guard !options.isEmpty, + let index = selectedIndex + else { return nil } + + return options[index] } //-------------------------------------------------- @@ -45,13 +48,16 @@ if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) { self.selectedIndex = selectedIndex } - baseValue = options.indices.contains(selectedIndex) ? options[selectedIndex] : nil + + if let index = selectedIndex { + baseValue = options.indices.contains(index) ? options[index] : nil + } } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(options, forKey: .options) - try container.encode(options, forKey: .selectedIndex) + try container.encodeIfPresent(options, forKey: .selectedIndex) } } From 84cb19805724a53e60a2ee012995f0ec861049ab Mon Sep 17 00:00:00 2001 From: Kyle Matthew Hedden Date: Fri, 18 Sep 2020 15:25:48 -0400 Subject: [PATCH 26/54] move the drop down action to field input dismissal. --- .../FormFields/TextFields/BaseDropdownEntryField.swift | 5 +++++ .../FormFields/TextFields/DateDropdownEntryField.swift | 1 - .../FormFields/TextFields/ItemDropdownEntryField.swift | 7 ------- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index 2076d0fc..38da2bdb 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -86,6 +86,11 @@ import UIKit dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) } + public override func dismissFieldInput(_ sender: Any?) { + performDropdownAction() + super.dismissFieldInput(sender) + } + func performDropdownAction() { if let actionModel = baseDropdownEntryFieldModel?.action, let actionMap = actionModel.toJSON() { var additionalData = self.additionalData ?? [:] diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift index b8fba094..6ff950f6 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DateDropdownEntryField.swift @@ -106,7 +106,6 @@ import UIKit @objc public override func dismissFieldInput(_ sender: Any?) { setTextWith(date: datePicker?.date) - performDropdownAction() super.dismissFieldInput(sender) } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift index 6c2a0f4e..ddc459c3 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift @@ -32,8 +32,6 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { return model as? ItemDropdownEntryFieldModel } - private var systemWillSetItem = false - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -112,9 +110,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { setPickerDelegates(delegate: self) if let pickerView = pickerView, let index = model.selectedIndex { - systemWillSetItem = true self.pickerView(pickerView, didSelectRow: index, inComponent: 0) - systemWillSetItem = false } } } @@ -142,9 +138,6 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { observeDropdownChange?(text ?? "", pickerData[row]) text = pickerData[row] itemDropdownEntryFieldModel?.selectedIndex = row - if !systemWillSetItem { - performDropdownAction() - } } } From c9726652b19626efd78b87c53bc5473b0059a357 Mon Sep 17 00:00:00 2001 From: Kyle Matthew Hedden Date: Fri, 18 Sep 2020 16:58:36 -0400 Subject: [PATCH 27/54] Add toggle model source. --- MVMCoreUI/Atomic/Atoms/Views/Toggle.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift index dfffcded..dd7dc9f0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift @@ -395,17 +395,19 @@ public typealias ActionBlockConfirmation = () -> (Bool) let actionMap = model.action?.toJSON() let alternateActionMap = model.alternateAction?.toJSON() if actionMap != nil || alternateActionMap != nil { + var additionalDatatoUpdate = additionalData ?? [:] + additionalDatatoUpdate[KeySourceModel] = model didToggleAction = { [weak self] in guard let self = self else { return } if self.isOn { if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } } else { if alternateActionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } else if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } } } From bf0fe53bc66c1b2abdd27a3cca5633a236ee631c Mon Sep 17 00:00:00 2001 From: Kyle Matthew Hedden Date: Fri, 18 Sep 2020 17:39:16 -0400 Subject: [PATCH 28/54] else if for clarity --- MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index b6a87cd9..edc37628 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -440,9 +440,8 @@ import MVMCore if let offAction = model.offAction, !self.isSelected { self.performCheckboxAction(with: offAction, delegateObject: delegateObject, additionalData: additionalData) - return - } - if let action = model.action { + + } else if let action = model.action { self.performCheckboxAction(with: action, delegateObject: delegateObject, additionalData: additionalData) } } From ea50b838d29e0fd6df7ce5bcb2593fb42f4ce3e1 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 21 Sep 2020 09:52:51 -0400 Subject: [PATCH 29/54] molecular --- MVMCoreUI.xcodeproj/project.pbxproj | 16 ++ MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 4 +- .../CollapsableNotification.swift | 168 ++++++++++++++++++ .../CollapsableNotificationModel.swift | 43 ++--- .../TopNotification/Notification.swift | 117 ++++++++++++ .../TopNotification/NotificationModel.swift | 52 +++++- .../NotificationStatusBar.swift | 56 ++++++ .../TopNotification/NotificationXButton.swift | 42 +++++ .../NotificationXButtonModel.swift | 23 ++- MVMCoreUI/BaseClasses/Button.swift | 4 +- .../MVMCoreUITopAlertView+Extension.swift | 12 +- MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h | 3 +- MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 16 +- 13 files changed, 503 insertions(+), 53 deletions(-) create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift create mode 100644 MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b67f6235..de7e8645 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -318,6 +318,7 @@ D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */; }; D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8392241C27B100D3DF69 /* TemplateModel.swift */; }; D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */; }; + D23118B325124E18001C8440 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23118B225124E18001C8440 /* Notification.swift */; }; D2351C7A24A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */; }; D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */; }; D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */; }; @@ -481,6 +482,9 @@ D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; }; D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; }; D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; }; + D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; }; + D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; }; + D2FA83D62515021F00564112 /* NotificationStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* NotificationStatusBar.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; }; DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; @@ -806,6 +810,7 @@ D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionMoleculeTableViewCell.swift; sourceTree = ""; }; D22D8392241C27B100D3DF69 /* TemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModel.swift; sourceTree = ""; }; D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; + D23118B225124E18001C8440 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinksModel.swift; sourceTree = ""; }; D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinks.swift; sourceTree = ""; }; D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescription.swift; sourceTree = ""; }; @@ -970,6 +975,9 @@ D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = ""; }; D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = ""; }; D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = ""; }; + D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = ""; }; + D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = ""; }; + D2FA83D52515021F00564112 /* NotificationStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationStatusBar.swift; sourceTree = ""; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = ""; }; D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = ""; }; DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = ""; }; @@ -2077,9 +2085,13 @@ isa = PBXGroup; children = ( D2CAC7CA251104E100C75681 /* NotificationXButtonModel.swift */, + D2FA83D12513EA6900564112 /* NotificationXButton.swift */, D2CAC7CC251104FE00C75681 /* NotificationModel.swift */, + D23118B225124E18001C8440 /* Notification.swift */, D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */, D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */, + D2FA83D32514F80C00564112 /* CollapsableNotification.swift */, + D2FA83D52515021F00564112 /* NotificationStatusBar.swift */, D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */, ); path = TopNotification; @@ -2460,6 +2472,7 @@ C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */, 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */, 011D958524042432000E3791 /* RulesProtocol.swift in Sources */, + D23118B325124E18001C8440 /* Notification.swift in Sources */, AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */, AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */, D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */, @@ -2515,6 +2528,7 @@ D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */, C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */, 0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */, + D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */, D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */, 0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */, 94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */, @@ -2560,6 +2574,7 @@ AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */, + D2FA83D62515021F00564112 /* NotificationStatusBar.swift in Sources */, 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */, AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */, D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */, @@ -2605,6 +2620,7 @@ C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */, + D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */, 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */, 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */, D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index a598b113..9ebb8cfb 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -233,8 +233,8 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: LockupsPlanSMLXL.self, viewModelClass: LockupsPlanSMLXLModel.self) // MARK: - Top Notifications - MoleculeObjectMapping.shared()?.register(viewClass: MVMCoreUITopAlertMainView.self, viewModelClass: NotificationModel.self) - MoleculeObjectMapping.shared()?.register(viewClass: MVMCoreUITopAlertExpandableView.self, viewModelClass: CollapsableNotificationModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: NotificationView.self, viewModelClass: NotificationModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: CollapsableNotification.self, viewModelClass: CollapsableNotificationModel.self) // MARK:- Helper models try? ModelRegistry.register(RuleRequiredModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift new file mode 100644 index 00000000..54eeb51a --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -0,0 +1,168 @@ +// +// CollapsableNotification.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class CollapsableNotification: View { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + public let topView = NotificationStatusBar() + public let bottomView = NotificationView() + public var verticalStack: UIStackView! + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + + public override func setupView() { + super.setupView() + + verticalStack = UIStackView(arrangedSubviews: [topView, bottomView]) + verticalStack.translatesAutoresizingMaskIntoConstraints = false + verticalStack.axis = .vertical + verticalStack.alignment = .fill + verticalStack.distribution = .fill + addSubview(verticalStack) + NSLayoutConstraint.constraintPinSubview(verticalStack, pinTop: true, topConstant: 0, pinBottom: true, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0) + + reset() + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + verticalStack.updateView(size) + } + + open override func reset() { + super.reset() + verticalStack.reset() + backgroundColor = .mvmGreen() + } + + //-------------------------------------------------- + // MARK: - Molecule + //-------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? CollapsableNotificationModel else { return } + topView.label.set(with: model.topLabel, delegateObject, additionalData) + topView.button.set(with: model.topAction, delegateObject: delegateObject, additionalData: additionalData) + bottomView.set(with: model, delegateObject, additionalData) + updateAccessibilityLabel() + + topView.isHidden = !model.alwaysShowTopLabel && !model.initiallyCollapsed + topView.button.isUserInteractionEnabled = model.initiallyCollapsed + bottomView.isHidden = model.initiallyCollapsed + verticalStack.layoutIfNeeded() + + if !model.initiallyCollapsed { + collapse(with: .now() + DispatchTimeInterval.seconds(model.collapseTime)) + } + } + + open func collapse(with delay: DispatchTime) { + DispatchQueue.main.asyncAfter(deadline: delay) { [weak self] in + self?.collapse() + } + } + + open func collapse(animated: Bool = true) { + let animation = { [weak self] in + self?.topView.isHidden = false + self?.bottomView.isHidden = true + self?.verticalStack.layoutIfNeeded() + } + if animated { + UIView.animate(withDuration: 0.5, animations: animation) { [weak self] (finished) in + self?.topView.button.isUserInteractionEnabled = true + } + } else { + animation() + } + } + + open func expand(topViewShowing: Bool = false, animated: Bool = true) { + topView.button.isUserInteractionEnabled = false + let animation = { [weak self] in + self?.topView.isHidden = !topViewShowing + self?.bottomView.isHidden = false + self?.verticalStack.layoutIfNeeded() + } + if animated { + UIView.animate(withDuration: 0.5, animations: animation) { [weak self] (finished) in + + } + } else { + animation() + } + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 96 + } + /* + func getAccessibilityMessage() -> String? { + + guard let leftImageLabel = leftImage.imageView.accessibilityLabel else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return leftImageLabel + } + + return leftImageLabel + ", " + label + }*/ + + func updateAccessibilityLabel() { + /*headline.accessibilityLabel = headline.text + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline) + + body.accessibilityLabel = body.text + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body) + + + let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 + isAccessibilityElement = !linkShowing + accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none + + if !linkShowing { + // Make whole cell focusable if no link. + accessibilityLabel = getAccessibilityMessage() + } else if let accessoryView = accessoryView { + // Both caret and link. Read all content on caret. + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] + } else { + // Only link. Manually add accessibility elements to ensure they are read in the right order. + var elements: [Any] = [] + + if let leftImageLabel = leftImage.imageView.accessibilityLabel, !leftImageLabel.isEmpty { + elements.append(leftImage.imageView) + } + + if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { + elements.append(otherElements) + } + + accessibilityElements = elements + }*/ + } +} + +extension CollapsableNotification: StatusBarUI { + func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) { + let color = backgroundColor ?? UIColor.mvmGreen + var greyScale: CGFloat = 0 + topView.label.textColor.getWhite(&greyScale, alpha: nil) + return (color, greyScale > 0.5 ? .lightContent : .default) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift index 15db1009..804bfeb6 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift @@ -8,16 +8,12 @@ import Foundation -public class CollapsableNotificationModel: MoleculeModelProtocol { - public static var identifier: String = "collapsableNotification" - public var moleculeName: String = CollapsableNotificationModel.identifier - public var backgroundColor: Color? +public class CollapsableNotificationModel: NotificationModel { + public class override var identifier: String { + return "collapsableNotification" + } public var topLabel: LabelModel public var topAction: ActionModelProtocol? - public var headline: LabelModel - public var body: LabelModel? - public var button: ButtonModel? - public var closeButton: NotificationXButtonModel? public var alwaysShowTopLabel = false public var collapseTime: Int = 5 public var initiallyCollapsed = false @@ -25,18 +21,23 @@ public class CollapsableNotificationModel: MoleculeModelProtocol { init(with topLabel: LabelModel, headline: LabelModel) { self.topLabel = topLabel - self.headline = headline + super.init(with: headline) + } + + override func setDefault() { + super.setDefault() + if topLabel.textColor == nil { + topLabel.textColor = Color(uiColor: .white) + } + if topLabel.textAlignment == nil { + topLabel.textAlignment = .center + } } private enum CodingKeys: String, CodingKey { case moleculeName - case backgroundColor case topLabel case topAction - case headline - case body - case button - case closeButton case alwaysShowTopLabel case collapseTime case initiallyCollapsed @@ -46,12 +47,7 @@ public class CollapsableNotificationModel: MoleculeModelProtocol { required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) topLabel = try typeContainer.decode(LabelModel.self, forKey: .topLabel) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) topAction = try typeContainer.decodeModelIfPresent(codingKey: .topAction) - headline = try typeContainer.decode(LabelModel.self, forKey: .headline) - body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) - button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) - closeButton = try typeContainer.decodeIfPresent(NotificationXButtonModel.self, forKey: .closeButton) if let alwaysShowTopLabel = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowTopLabel) { self.alwaysShowTopLabel = alwaysShowTopLabel } @@ -62,18 +58,15 @@ public class CollapsableNotificationModel: MoleculeModelProtocol { self.initiallyCollapsed = initiallyCollapsed } pages = try typeContainer.decodeIfPresent([String].self, forKey: .pages) + try super.init(from: decoder) } - public func encode(to encoder: Encoder) throws { + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encode(topLabel, forKey: .topLabel) try container.encodeModelIfPresent(topAction, forKey: .topAction) - try container.encode(headline, forKey: .headline) - try container.encodeIfPresent(body, forKey: .body) - try container.encodeIfPresent(button, forKey: .button) - try container.encodeIfPresent(closeButton, forKey: .closeButton) try container.encode(alwaysShowTopLabel, forKey: .alwaysShowTopLabel) try container.encode(collapseTime, forKey: .collapseTime) try container.encode(initiallyCollapsed, forKey: .initiallyCollapsed) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift new file mode 100644 index 00000000..823158fc --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift @@ -0,0 +1,117 @@ +// +// Notification.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class NotificationView: View { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + public let headline = Label(fontStyle: .BoldBodySmall) + public let body = Label(fontStyle: .RegularBodySmall) + public let button = PillButton(asPrimaryButton: false, makeTiny: true) + public let closeButton = NotificationXButton() + public var labelStack: Stack! + public var horizontalStack: Stack! + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + + public override func setupView() { + super.setupView() + reset() + + labelStack = Stack.createStack(with: [headline, body], spacing: 0) + horizontalStack = Stack.createStack(with: [(view: labelStack, model: StackItemModel()),(view: button, model: StackItemModel(horizontalAlignment: .fill)),(view: closeButton, model: StackItemModel(horizontalAlignment: .fill))], axis: .horizontal) + addSubview(horizontalStack) + NSLayoutConstraint.constraintPinSubview(horizontalStack, pinTop: true, topConstant: PaddingTwo, pinBottom: true, bottomConstant: PaddingTwo, pinLeft: true, leftConstant: PaddingThree, pinRight: true, rightConstant: PaddingThree) + labelStack.restack() + horizontalStack.restack() + + heightAnchor.constraint(equalToConstant: 96).isActive = true + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + horizontalStack.updateView(size) + } + + open override func reset() { + super.reset() + backgroundColor = .mvmGreen() + headline.textColor = .white + body.textColor = .white + } + + //-------------------------------------------------- + // MARK: - Molecule + //-------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? NotificationModel else { return } + labelStack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, nil) + horizontalStack.updateContainedMolecules(with: [labelStack.stackModel, model.button, model.closeButton], delegateObject, nil) + + updateAccessibilityLabel() + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 96 + } + /* + func getAccessibilityMessage() -> String? { + + guard let leftImageLabel = leftImage.imageView.accessibilityLabel else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return leftImageLabel + } + + return leftImageLabel + ", " + label + }*/ + + func updateAccessibilityLabel() { + /*headline.accessibilityLabel = headline.text + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline) + + body.accessibilityLabel = body.text + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body) + + + let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 + isAccessibilityElement = !linkShowing + accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none + + if !linkShowing { + // Make whole cell focusable if no link. + accessibilityLabel = getAccessibilityMessage() + } else if let accessoryView = accessoryView { + // Both caret and link. Read all content on caret. + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] + } else { + // Only link. Manually add accessibility elements to ensure they are read in the right order. + var elements: [Any] = [] + + if let leftImageLabel = leftImage.imageView.accessibilityLabel, !leftImageLabel.isEmpty { + elements.append(leftImage.imageView) + } + + if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { + elements.append(otherElements) + } + + accessibilityElements = elements + }*/ + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift index ac74abb6..e2f94326 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift @@ -9,8 +9,9 @@ import Foundation public class NotificationModel: MoleculeModelProtocol { - public static var identifier: String = "notification" - public var moleculeName: String = NotificationModel.identifier + public class var identifier: String { + return "notification" + } public var backgroundColor: Color? public var headline: LabelModel public var body: LabelModel? @@ -20,4 +21,51 @@ public class NotificationModel: MoleculeModelProtocol { init(with headline: LabelModel) { self.headline = headline } + + func setDefault() { + if backgroundColor == nil { + backgroundColor = Color(uiColor: .mvmGreen()) + } + if headline.textColor == nil { + headline.textColor = Color(uiColor: .white) + } + if body?.textColor == nil { + body?.textColor = Color(uiColor: .white) + } + if button?.style == nil { + button?.style = .secondary + } + button?.size = .tiny + button?.enabledTextColor = Color(uiColor: .white) + button?.enabledBorderColor = Color(uiColor: .white) + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case headline + case body + case button + case closeButton + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + headline = try typeContainer.decode(LabelModel.self, forKey: .headline) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) + button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) + closeButton = try typeContainer.decodeIfPresent(NotificationXButtonModel.self, forKey: .closeButton) + setDefault() + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(headline, forKey: .headline) + try container.encodeIfPresent(body, forKey: .body) + try container.encodeIfPresent(button, forKey: .button) + try container.encodeIfPresent(closeButton, forKey: .closeButton) + } } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift new file mode 100644 index 00000000..73bde3f1 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift @@ -0,0 +1,56 @@ +// +// NotificationStatusBar.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class NotificationStatusBar: View { + public let label: Label = { + let label = Label(fontStyle: .BoldBodySmall) + label.numberOfLines = 1 + label.textAlignment = .center + label.setContentHuggingPriority(.defaultHigh, for: .vertical) + return label + }() + + public let button: Button = { + let button = Button(type: .custom) + button.backgroundColor = .clear + button.setContentCompressionResistancePriority(.defaultLow, for: .vertical) + button.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + return button + }() + + public override func setupView() { + super.setupView() + + addSubview(label) + NSLayoutConstraint.constraintPinSubview(label, pinTop: true, topConstant: 0, pinBottom: true, bottomConstant: 0, pinLeft: true, leftConstant: PaddingThree, pinRight: true, rightConstant: PaddingThree) + + addSubview(button) + NSLayoutConstraint.constraintPinSubview(button, pinTop: true, topConstant: 0, pinBottom: true, bottomConstant: -5, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0) + + // Listen for status bar touches. + NotificationCenter.default.addObserver(self, selector: #selector(pressed(_:)), name: NSNotification.Name(rawValue: NotificationStatusBarTouched), object: nil) + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + label.updateView(size) + } + + open override func reset() { + super.reset() + label.setFontStyle(.BoldBodySmall) + label.textColor = .white + label.textAlignment = .center + } + + @objc func pressed(_ sender: Notification) { + button.callActionBlock(button) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift new file mode 100644 index 00000000..434ddc3d --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift @@ -0,0 +1,42 @@ +// +// NotificationXButton.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/17/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class NotificationXButton: Button { + + open func closeTopAlert() { + if let delegate = MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate { + delegate.topAlertCloseButtonPressed() + } else { + MVMCoreUISession.sharedGlobal()?.topAlertView?.hideAlertView(true, completionHandler: nil) + } + } + + open override func setupView() { + if let image = MVMCoreUIUtility.imageNamed("nav_close")?.withRenderingMode(.alwaysTemplate) { + setImage(image, for: .normal) + } + tintColor = .white + adjustsImageWhenHighlighted = false + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "AccCloseButton") + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? NotificationXButtonModel else { return } + tintColor = model.color.uiColor + + // temporary + if model.action.actionType == ActionNoopModel.identifier { + addActionBlock(event: .touchUpInside) { (button) in + (button as? NotificationXButton)?.closeTopAlert() + } + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButtonModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButtonModel.swift index 368ec1cf..81dae17e 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButtonModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButtonModel.swift @@ -8,27 +8,32 @@ import Foundation -public class NotificationXButtonModel: Codable { - public var color: Color? - public var action: ActionModelProtocol? +public class NotificationXButtonModel: ButtonModelProtocol, MoleculeModelProtocol { + public static var identifier: String = "notificationXButton" + public var backgroundColor: Color? + public var color = Color(uiColor: .white) + public var action: ActionModelProtocol = ActionNoopModel() private enum CodingKeys: String, CodingKey { + case moleculeName case color case action } - public init() { - } + public init() {} public required init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) - action = try typeContainer.decodeModelIfPresent(codingKey: .action) + color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) ?? Color(uiColor: .white) + if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) { + self.action = action + } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeIfPresent(color, forKey: .color) - try container.encodeModelIfPresent(action, forKey: .action) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(color, forKey: .color) + try container.encodeModel(action, forKey: .action) } } diff --git a/MVMCoreUI/BaseClasses/Button.swift b/MVMCoreUI/BaseClasses/Button.swift index 757855ed..535d3830 100644 --- a/MVMCoreUI/BaseClasses/Button.swift +++ b/MVMCoreUI/BaseClasses/Button.swift @@ -72,12 +72,12 @@ public typealias ButtonAction = (Button) -> () buttonAction?(self) } - open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + open func set(with actionModel: ActionModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.actionModel = actionModel buttonDelegate = delegateObject?.buttonDelegate addActionBlock(event: .touchUpInside) { [weak self] sender in - guard let self = self else { return } + guard let self = self, let actionModel = actionModel else { return } Self.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) } } diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index e6df7178..09cef092 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -56,21 +56,23 @@ public extension MVMCoreUITopAlertView { } /// Returns the top alert molecule to use and status bar color legacy style. - @objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer?, statusBarStyle: UnsafeMutablePointer?) -> MVMCoreUITopAlertBaseView? { + @objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer?, statusBarStyle: UnsafeMutablePointer?) -> UIView? { do { let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) guard let json = topAlertObject.json else { return nil } let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject) - guard let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil), - let view = molecule as? MVMCoreUITopAlertBaseView else { + guard let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil)/*, + let view = molecule as? MVMCoreUITopAlertBaseView*/ else { throw ModelRegistry.Error.decoderOther(message: "Molecule not a top alert") } - if let castView = view as? StatusBarUI { + if let castView = molecule as? StatusBarUI { let (color, style) = castView.getStatusBarUI() statusBarColor?.pointee = color statusBarStyle?.pointee = style } - return view + // Temporary + molecule.heightAnchor.constraint(lessThanOrEqualToConstant: 150).isActive = true + return molecule } catch { if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h index 4b757bf7..82c0a7e9 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h @@ -16,7 +16,6 @@ #import @class MVMCoreTopAlertObject; -@class MVMCoreUITopAlertBaseView; @interface MVMCoreUITopAlertView : UIView @@ -45,7 +44,7 @@ - (void)resetDefaultBackgroundColor:(nullable UIColor *)backgroundColor basedOnStatusBarStyle:(UIStatusBarStyle)style; // Can be subclassed for custom views. -- (nonnull MVMCoreUITopAlertBaseView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle; +- (nonnull UIView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle; /// Get the background color based on the type - (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 903a24d3..570b9297 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -124,7 +124,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; } } -- (nonnull MVMCoreUITopAlertBaseView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle { +- (nonnull UIView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle { if (topAlertObject.json) { return [self moleculeFor:topAlertObject statusBarColor:statusBarColor statusBarStyle:statusBarStyle]; } else { @@ -169,8 +169,10 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; UIColor *statusBarColor = nil; UIStatusBarStyle statusBarStyle = UIStatusBarStyleDefault; - MVMCoreUITopAlertBaseView *view = [self topAlertViewForTopAlertObject:topAlertObject animationDelegate:animationDelegate statusBarColor:&statusBarColor statusBarStyle:&statusBarStyle]; - [view updateView:CGRectGetWidth(self.bounds)]; + UIView *view = [self topAlertViewForTopAlertObject:topAlertObject animationDelegate:animationDelegate statusBarColor:&statusBarColor statusBarStyle:&statusBarStyle]; + if ([view conformsToProtocol:@protocol(MVMCoreViewProtocol)]) { + [((UIView *)view) updateView:CGRectGetWidth(self.bounds)]; + } if (!statusBarColor) { statusBarColor = [UIColor whiteColor]; } @@ -185,7 +187,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; [[MVMCoreUISession sharedGlobal].splitViewController.parentViewController setNeedsStatusBarAppearanceUpdate]; } -- (void)showAlertView:(nullable MVMCoreUITopAlertBaseView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { +- (void)showAlertView:(nullable UIView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { __weak typeof(self) weakSelf = self; MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { @@ -205,8 +207,10 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; } completion:^(BOOL finished) { [weakSelf.superview layoutIfNeeded]; [weakSelf.animationDelegate topAlertViewFinishAnimation]; - [view handleAccessibility]; - + if ([view isKindOfClass:[MVMCoreUITopAlertBaseView class]]) { + [((MVMCoreUITopAlertBaseView *)view) handleAccessibility]; + } + [MVMCoreDispatchUtility performBlockInBackground:^{ if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewShown:topAlertObject:)]) { [weakSelf.topAlertObject.delegate topAlertViewShown:view topAlertObject:topAlertObject]; From 324605cdeba16e0ac46331af9d9a9ddc8eacad9d Mon Sep 17 00:00:00 2001 From: Kyle Matthew Hedden Date: Mon, 21 Sep 2020 12:17:25 -0400 Subject: [PATCH 30/54] prevent double selection and code review items --- .../FormFields/TextFields/BaseDropdownEntryField.swift | 7 +++---- MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift | 8 +++----- MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift | 7 +++---- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift | 8 +++----- MVMCoreUI/Atomic/Atoms/Views/Toggle.swift | 9 ++++----- MVMCoreUI/BaseClasses/Button.swift | 9 +++++++-- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index 38da2bdb..82e46306 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -92,10 +92,9 @@ import UIKit } func performDropdownAction() { - if let actionModel = baseDropdownEntryFieldModel?.action, let actionMap = actionModel.toJSON() { - var additionalData = self.additionalData ?? [:] - additionalData[KeySourceModel] = baseDropdownEntryFieldModel - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + if let baseDropdownEntryFieldModel = baseDropdownEntryFieldModel, let actionModel = baseDropdownEntryFieldModel.action, let actionMap = actionModel.toJSON() { + let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: baseDropdownEntryFieldModel) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index 46b4cef5..d4fa0232 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -137,13 +137,11 @@ open class RadioBox: Control, MFButtonProtocol { } @objc open func selectBox() { - guard isEnabled else { return } + guard isEnabled, !isSelected else { return } isSelected = true radioBoxModel?.selected = isSelected - if let actionModel = radioBoxModel?.action { - var additionalData = self.additionalData ?? [:] - additionalData[KeySourceModel] = radioBoxModel - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) + if let radioBoxModel = radioBoxModel, let actionModel = radioBoxModel.action { + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioBoxModel) } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index 9e18f936..bd720be8 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -95,15 +95,14 @@ import UIKit if !isEnabled { return } + let wasPreviouslySelected = isSelected if let radioButtonModel = radioButtonSelectionHelper { radioButtonModel.selected(self) } else { isSelected = !isSelected } - if let actionModel = radioModel?.action, isSelected { - var additionalData = self.additionalData ?? [:] - additionalData[KeySourceModel] = radioModel - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) + 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() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index e1502515..70f2a459 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -119,13 +119,11 @@ open class RadioSwatch: Control, MFButtonProtocol { } @objc open func selectSwatch() { - guard isEnabled else { return } + guard isEnabled, !isSelected else { return } isSelected = true radioSwatchModel?.selected = isSelected - if let actionModel = radioSwatchModel?.action { - var additionalData = self.additionalData ?? [:] - additionalData[KeySourceModel] = radioSwatchModel - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) + if let radioSwatchModel = radioSwatchModel, let actionModel = radioSwatchModel.action { + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioSwatchModel) } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift index dd7dc9f0..5712695b 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift @@ -394,20 +394,19 @@ public typealias ActionBlockConfirmation = () -> (Bool) let actionMap = model.action?.toJSON() let alternateActionMap = model.alternateAction?.toJSON() + let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) if actionMap != nil || alternateActionMap != nil { - var additionalDatatoUpdate = additionalData ?? [:] - additionalDatatoUpdate[KeySourceModel] = model didToggleAction = { [weak self] in guard let self = self else { return } if self.isOn { if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) } } else { if alternateActionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) } else if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/BaseClasses/Button.swift b/MVMCoreUI/BaseClasses/Button.swift index 757855ed..599fc48c 100644 --- a/MVMCoreUI/BaseClasses/Button.swift +++ b/MVMCoreUI/BaseClasses/Button.swift @@ -82,11 +82,16 @@ public typealias ButtonAction = (Button) -> () } } - open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, sourceModel: MoleculeModelProtocol? = nil) { if let data = try? model.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any], delegateObject?.buttonDelegate?.button?(button, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + if let sourceModel = sourceModel { + let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: sourceModel) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) + } else { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } } } From 4986bb09a9e3a40d6ad6762bab5a520b94c7f1c9 Mon Sep 17 00:00:00 2001 From: Kyle Matthew Hedden Date: Mon, 21 Sep 2020 16:11:58 -0400 Subject: [PATCH 31/54] Add array protectioons. --- MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m index e0eef9d6..9a1d3099 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m @@ -393,8 +393,12 @@ weakSelf.buttonView.label.alpha = 0; weakSelf.shortViewHeight.active = NO; } completion:^(BOOL finished) { - [weakSelf.viewToLayout layoutIfNeeded]; - weakSelf.accessibilityElements = @[weakSelf.shortView.label]; + typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + [strongSelf.viewToLayout layoutIfNeeded]; + strongSelf.accessibilityElements = @[strongSelf.shortView.label]; UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); [MVMCoreDispatchUtility performBlockInBackground:^{ // Must notify animation delegate when animating finished. From cba21e3b4458b9ce4d4a53c33dc447a02c25b19a Mon Sep 17 00:00:00 2001 From: Kyle Matthew Hedden Date: Mon, 21 Sep 2020 17:41:59 -0400 Subject: [PATCH 32/54] extract method for animations. --- .../MVMCoreUITopAlertExpandableView.m | 154 +++++++++--------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m index 9a1d3099..76ddb1ed 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m @@ -298,58 +298,64 @@ __weak typeof(self) weakSelf = self; MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { [MVMCoreDispatchUtility performBlockOnMainThread:^{ - // Must notify animation delegate before animating. - if (animated && weakSelf.animationDelegate) { - [weakSelf.animationDelegate topAlertViewBeginAnimation]; - } - - [weakSelf.viewToLayout layoutIfNeeded]; - weakSelf.topLabelConstraintBottom.active = NO; - weakSelf.topConstraint.active = YES; - weakSelf.expanded = YES; - - void(^animation)(void) = ^(void) { - weakSelf.buttonView.button.alpha = 1; - weakSelf.buttonView.label.alpha = 1; - if (weakSelf.onlyShowTopMessageWhenCollapsed) { - weakSelf.shortViewHeight.active = YES; - } - [weakSelf.viewToLayout layoutIfNeeded]; - }; - - //accessibility - added to make only top alert label and close button accessible. Posted notification when top alert is displayed - weakSelf.accessibilityElements = @[weakSelf.buttonView]; - weakSelf.shortView.isAccessibilityElement = NO; - - void(^completion)(void) = ^(void) { - UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, weakSelf.buttonView.label); + [weakSelf performExpansion:animated onCompletion:^{ [operation markAsFinished]; - }; - if (animated) { - [UIView animateWithDuration:.5 animations:animation completion:^(BOOL finished) { - [weakSelf.viewToLayout layoutIfNeeded]; - - // Must notify animation delegate when animating finished. - [MVMCoreDispatchUtility performBlockInBackground:^{ - if (weakSelf.animationDelegate) { - [weakSelf.animationDelegate topAlertViewFinishAnimation]; - } - }]; - completion(); - }]; - } else { - animation(); - completion(); - } - - // Collapse after 5 seconds (if the view still exists) - [weakSelf autoCollapse]; + }]; }]; }]; [[MVMCoreNavigationHandler sharedNavigationHandler] addNavigationOperation:operation]; } } +- (void)performExpansion:(BOOL)animated onCompletion:(void(^)(void))completionHandler { + // Must notify animation delegate before animating. + if (animated && self.animationDelegate) { + [self.animationDelegate topAlertViewBeginAnimation]; + } + + [self.viewToLayout layoutIfNeeded]; + self.topLabelConstraintBottom.active = NO; + self.topConstraint.active = YES; + self.expanded = YES; + + void(^animation)(void) = ^(void) { + self.buttonView.button.alpha = 1; + self.buttonView.label.alpha = 1; + if (self.onlyShowTopMessageWhenCollapsed) { + self.shortViewHeight.active = YES; + } + [self.viewToLayout layoutIfNeeded]; + }; + + //accessibility - added to make only top alert label and close button accessible. Posted notification when top alert is displayed + self.accessibilityElements = @[self.buttonView]; + self.shortView.isAccessibilityElement = NO; + + void(^completion)(void) = ^(void) { + UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.buttonView.label); + completionHandler(); + }; + if (animated) { + [UIView animateWithDuration:.5 animations:animation completion:^(BOOL finished) { + [self.viewToLayout layoutIfNeeded]; + + // Must notify animation delegate when animating finished. + [MVMCoreDispatchUtility performBlockInBackground:^{ + if (self.animationDelegate) { + [self.animationDelegate topAlertViewFinishAnimation]; + } + }]; + completion(); + }]; + } else { + animation(); + completion(); + } + + // Collapse after 5 seconds (if the view still exists) + [self autoCollapse]; +} + - (void)autoCollapse { if (self.collapseAutomaticallyAfterExpanded) { __weak typeof(self) weakSelf = self; @@ -379,34 +385,8 @@ __weak typeof(self) weakSelf = self; MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { [MVMCoreDispatchUtility performBlockOnMainThread:^{ - // Must notify animation delegate before animating. - if (weakSelf.animationDelegate) { - [weakSelf.animationDelegate topAlertViewBeginAnimation]; - } - [weakSelf.viewToLayout layoutIfNeeded]; - weakSelf.topConstraint.active = NO; - weakSelf.topLabelConstraintBottom.active = YES; - weakSelf.expanded = NO; - [UIView animateWithDuration:.5 animations:^{ - [weakSelf.viewToLayout layoutIfNeeded]; - weakSelf.buttonView.button.alpha = 0; - weakSelf.buttonView.label.alpha = 0; - weakSelf.shortViewHeight.active = NO; - } completion:^(BOOL finished) { - typeof(self) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - [strongSelf.viewToLayout layoutIfNeeded]; - strongSelf.accessibilityElements = @[strongSelf.shortView.label]; - UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); - [MVMCoreDispatchUtility performBlockInBackground:^{ - // Must notify animation delegate when animating finished. - if (weakSelf.animationDelegate) { - [weakSelf.animationDelegate topAlertViewFinishAnimation]; - } - [operation markAsFinished]; - }]; + [weakSelf performCollapseAnimationThen:^{ + [operation markAsFinished]; }]; }]; }]; @@ -414,6 +394,34 @@ } } +- (void)performCollapseAnimationThen:(void(^)(void))completionHandler { + // Must notify animation delegate before animating. + if (self.animationDelegate) { + [self.animationDelegate topAlertViewBeginAnimation]; + } + [self.viewToLayout layoutIfNeeded]; + self.topConstraint.active = NO; + self.topLabelConstraintBottom.active = YES; + self.expanded = NO; + [UIView animateWithDuration:.5 animations:^{ + [self.viewToLayout layoutIfNeeded]; + self.buttonView.button.alpha = 0; + self.buttonView.label.alpha = 0; + self.shortViewHeight.active = NO; + } completion:^(BOOL finished) { + [self.viewToLayout layoutIfNeeded]; + self.accessibilityElements = @[self.shortView.label]; + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); + // Must notify animation delegate when animating finished. + [MVMCoreDispatchUtility performBlockInBackground:^{ + if (self.animationDelegate) { + [self.animationDelegate topAlertViewFinishAnimation]; + } + }]; + completionHandler(); + }]; +} + - (void)accessibilityFocusChanged:(NSNotification *)notification { if (![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil]; From f771469bc5b067712ed041c624ff2a63dcc7350d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Sep 2020 09:11:19 -0400 Subject: [PATCH 33/54] Molecular top alerts --- MVMCoreUI.xcodeproj/project.pbxproj | 12 +- .../CollapsableNotification.swift | 162 ++++++++++-------- ...t => CollapsableNotificationTopView.swift} | 11 +- .../TopNotification/Notification.swift | 55 +----- .../Protocols/AccessibilityProtocol.swift | 14 ++ .../MVMCoreUISplitViewController.m | 11 +- MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 18 +- MVMCoreUI/Utility/MVMCoreUIUtility.m | 6 +- 8 files changed, 155 insertions(+), 134 deletions(-) rename MVMCoreUI/Atomic/Molecules/TopNotification/{NotificationStatusBar.swift => CollapsableNotificationTopView.swift} (82%) create mode 100644 MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index de7e8645..404ae487 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -484,9 +484,10 @@ D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; }; D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; }; D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; }; - D2FA83D62515021F00564112 /* NotificationStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* NotificationStatusBar.swift */; }; + D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; }; + D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */; }; DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; @@ -977,9 +978,10 @@ D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = ""; }; D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = ""; }; D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = ""; }; - D2FA83D52515021F00564112 /* NotificationStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationStatusBar.swift; sourceTree = ""; }; + D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationTopView.swift; sourceTree = ""; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = ""; }; D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = ""; }; + D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityProtocol.swift; sourceTree = ""; }; DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = ""; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; @@ -1067,6 +1069,7 @@ children = ( D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */, 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, + D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */, ); path = Protocols; sourceTree = ""; @@ -2091,7 +2094,7 @@ D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */, D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */, D2FA83D32514F80C00564112 /* CollapsableNotification.swift */, - D2FA83D52515021F00564112 /* NotificationStatusBar.swift */, + D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */, D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */, ); path = TopNotification; @@ -2400,6 +2403,7 @@ AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */, 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */, + D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */, D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */, D20923592450ECE00044AD09 /* TableView.swift in Sources */, @@ -2574,7 +2578,7 @@ AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */, - D2FA83D62515021F00564112 /* NotificationStatusBar.swift in Sources */, + D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */, 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */, AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */, D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index 54eeb51a..6c8d84b8 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -13,7 +13,7 @@ import Foundation // MARK: - Outlets //-------------------------------------------------- - public let topView = NotificationStatusBar() + public let topView = CollapsableNotificationTopView() public let bottomView = NotificationView() public var verticalStack: UIStackView! @@ -53,108 +53,112 @@ import Foundation open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? CollapsableNotificationModel else { return } + topView.label.set(with: model.topLabel, delegateObject, additionalData) topView.button.set(with: model.topAction, delegateObject: delegateObject, additionalData: additionalData) + topView.updateAccessibility() + bottomView.set(with: model, delegateObject, additionalData) - updateAccessibilityLabel() + // Set initial collapse/expand state. topView.isHidden = !model.alwaysShowTopLabel && !model.initiallyCollapsed topView.button.isUserInteractionEnabled = model.initiallyCollapsed bottomView.isHidden = model.initiallyCollapsed verticalStack.layoutIfNeeded() if !model.initiallyCollapsed { - collapse(with: .now() + DispatchTimeInterval.seconds(model.collapseTime)) + autoCollapse() } } - open func collapse(with delay: DispatchTime) { - DispatchQueue.main.asyncAfter(deadline: delay) { [weak self] in - self?.collapse() + open func performBlockOperation(with block: @escaping (MVMCoreBlockOperation) -> Void) { + let operation = MVMCoreBlockOperation(block: block)! + MVMCoreNavigationHandler.shared()?.addNavigationOperation(operation) + } + + /// Collapses after a delay + open func autoCollapse() { + let delay: DispatchTimeInterval = DispatchTimeInterval.seconds((model as? CollapsableNotificationModel)?.collapseTime ?? 5) + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in + // If accessibility focused, delay collapse. + guard let localSelf = self else { return } + if MVMCoreUIUtility.viewContainsAccessiblityFocus(localSelf) { + NotificationCenter.default.addObserver(localSelf, selector: #selector(localSelf.accessibilityFocusChanged(notification:)), name: UIAccessibility.elementFocusedNotification, object: nil) + } else { + localSelf.collapse() + } } } + /// Collapses to show just the top view. open func collapse(animated: Bool = true) { - let animation = { [weak self] in - self?.topView.isHidden = false - self?.bottomView.isHidden = true - self?.verticalStack.layoutIfNeeded() - } - if animated { - UIView.animate(withDuration: 0.5, animations: animation) { [weak self] (finished) in - self?.topView.button.isUserInteractionEnabled = true - } - } else { - animation() + guard !bottomView.isHidden else { return } + performBlockOperation { [weak self] (operation) in + let strongSelf = self + MVMCoreDispatchUtility.performBlock(onMainThread: { + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + let animation = { + strongSelf?.topView.isHidden = false + strongSelf?.bottomView.isHidden = true + strongSelf?.verticalStack.layoutIfNeeded() + } + let completion: (Bool) -> Void = { (finished) in + strongSelf?.topView.button.isUserInteractionEnabled = true + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument()) + operation.markAsFinished() + } + + if animated { + UIView.animate(withDuration: 0.5, animations: animation, completion: completion) + } else { + animation() + completion(true) + } + }) } } + /// Expands to show the bottom view. open func expand(topViewShowing: Bool = false, animated: Bool = true) { - topView.button.isUserInteractionEnabled = false - let animation = { [weak self] in - self?.topView.isHidden = !topViewShowing - self?.bottomView.isHidden = false - self?.verticalStack.layoutIfNeeded() - } - if animated { - UIView.animate(withDuration: 0.5, animations: animation) { [weak self] (finished) in + guard bottomView.isHidden else { return } + performBlockOperation { [weak self] (operation) in + let strongSelf = self + MVMCoreDispatchUtility.performBlock(onMainThread: { + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + strongSelf?.topView.button.isUserInteractionEnabled = false + let animation = { + strongSelf?.topView.isHidden = !topViewShowing + strongSelf?.bottomView.isHidden = false + strongSelf?.verticalStack.layoutIfNeeded() + } + let completion: (Bool) -> Void = { (finished) in + MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded() + UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument()) + strongSelf?.autoCollapse() + operation.markAsFinished() + } - } - } else { - animation() + if animated { + UIView.animate(withDuration: 0.5, animations: animation, completion: completion) + } else { + animation() + completion(true) + } + }) } } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 96 } - /* - func getAccessibilityMessage() -> String? { - - guard let leftImageLabel = leftImage.imageView.accessibilityLabel else { - return eyebrowHeadlineBodyLink.getAccessibilityMessage() + + /// Collapse if focus is no longer on this top alert. + @objc func accessibilityFocusChanged(notification: Notification) { + if !MVMCoreUIUtility.viewContainsAccessiblityFocus(self) { + NotificationCenter.default.removeObserver(self, name: UIAccessibility.elementFocusedNotification, object: nil) + collapse() } - - guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { - return leftImageLabel - } - - return leftImageLabel + ", " + label - }*/ - - func updateAccessibilityLabel() { - /*headline.accessibilityLabel = headline.text - MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline) - - body.accessibilityLabel = body.text - MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body) - - - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 - isAccessibilityElement = !linkShowing - accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none - - if !linkShowing { - // Make whole cell focusable if no link. - accessibilityLabel = getAccessibilityMessage() - } else if let accessoryView = accessoryView { - // Both caret and link. Read all content on caret. - accessoryView.accessibilityLabel = getAccessibilityMessage() - accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] - } else { - // Only link. Manually add accessibility elements to ensure they are read in the right order. - var elements: [Any] = [] - - if let leftImageLabel = leftImage.imageView.accessibilityLabel, !leftImageLabel.isEmpty { - elements.append(leftImage.imageView) - } - - if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { - elements.append(otherElements) - } - - accessibilityElements = elements - }*/ } } @@ -166,3 +170,13 @@ extension CollapsableNotification: StatusBarUI { return (color, greyScale > 0.5 ? .lightContent : .default) } } + +extension CollapsableNotification: AccessibilityProtocol { + public func getAccessibilityLayoutChangedArgument() -> Any? { + if !topView.isHidden { + return topView + } else { + return bottomView.headline + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift similarity index 82% rename from MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift rename to MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift index 73bde3f1..36ddc8a6 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationStatusBar.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift @@ -1,5 +1,5 @@ // -// NotificationStatusBar.swift +// CollapsableNotificationTopView.swift // MVMCoreUI // // Created by Scott Pfeil on 9/18/20. @@ -8,7 +8,7 @@ import Foundation -@objcMembers open class NotificationStatusBar: View { +@objcMembers open class CollapsableNotificationTopView: View { public let label: Label = { let label = Label(fontStyle: .BoldBodySmall) label.numberOfLines = 1 @@ -50,6 +50,13 @@ import Foundation label.textAlignment = .center } + open func updateAccessibility() { + isAccessibilityElement = true + accessibilityLabel = label.text + accessibilityTraits = (button.isUserInteractionEnabled && button.actionModel != nil) ? .button : .none + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: self) + } + @objc func pressed(_ sender: Notification) { button.callActionBlock(button) } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift index 823158fc..c7d869c2 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift @@ -59,59 +59,22 @@ import Foundation guard let model = model as? NotificationModel else { return } labelStack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, nil) horizontalStack.updateContainedMolecules(with: [labelStack.stackModel, model.button, model.closeButton], delegateObject, nil) - - updateAccessibilityLabel() + updateAccessibility() } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 96 } - /* - func getAccessibilityMessage() -> String? { - - guard let leftImageLabel = leftImage.imageView.accessibilityLabel else { - return eyebrowHeadlineBodyLink.getAccessibilityMessage() - } - - guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { - return leftImageLabel - } - - return leftImageLabel + ", " + label - }*/ - func updateAccessibilityLabel() { - /*headline.accessibilityLabel = headline.text + open func updateAccessibility() { MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline) - - body.accessibilityLabel = body.text MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body) - - - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 - isAccessibilityElement = !linkShowing - accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none - - if !linkShowing { - // Make whole cell focusable if no link. - accessibilityLabel = getAccessibilityMessage() - } else if let accessoryView = accessoryView { - // Both caret and link. Read all content on caret. - accessoryView.accessibilityLabel = getAccessibilityMessage() - accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] - } else { - // Only link. Manually add accessibility elements to ensure they are read in the right order. - var elements: [Any] = [] - - if let leftImageLabel = leftImage.imageView.accessibilityLabel, !leftImageLabel.isEmpty { - elements.append(leftImage.imageView) - } - - if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { - elements.append(otherElements) - } - - accessibilityElements = elements - }*/ + MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: button) + } +} + +extension NotificationView: AccessibilityProtocol { + public func getAccessibilityLayoutChangedArgument() -> Any? { + return headline } } diff --git a/MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift b/MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift new file mode 100644 index 00000000..6c7ada50 --- /dev/null +++ b/MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift @@ -0,0 +1,14 @@ +// +// AccessibilityProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/21/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objc public protocol AccessibilityProtocol { + /// Should return the argument to use for posting a layout change. + func getAccessibilityLayoutChangedArgument() -> Any? +} diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 06e44c73..288878b8 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -1073,10 +1073,13 @@ CGFloat const PanelAnimationDuration = 0.2; } - (UIViewController *)getCurrentDetailViewController { - UIViewController *viewController = self.navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + __block UIViewController *viewController = nil; + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ + viewController = self.navigationController.topViewController; + if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { + viewController = [viewController performSelector:@selector(getCurrentViewController)]; + } + }]; return viewController; } diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 570b9297..31899d3f 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -187,6 +187,19 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; [[MVMCoreUISession sharedGlobal].splitViewController.parentViewController setNeedsStatusBarAppearanceUpdate]; } +- (void)updateAccessibilityForTopAlert:(nullable UIView *)view { + // Update accessibility with top alert + if ([view isKindOfClass:[MVMCoreUITopAlertBaseView class]]) { + [((MVMCoreUITopAlertBaseView *)view) handleAccessibility]; + } else { + id accessibilityArgument = view; + if ([view conformsToProtocol:@protocol(AccessibilityProtocol)]) { + accessibilityArgument = [((id )view) getAccessibilityLayoutChangedArgument]; + } + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, accessibilityArgument); + } +} + - (void)showAlertView:(nullable UIView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { __weak typeof(self) weakSelf = self; @@ -207,9 +220,8 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; } completion:^(BOOL finished) { [weakSelf.superview layoutIfNeeded]; [weakSelf.animationDelegate topAlertViewFinishAnimation]; - if ([view isKindOfClass:[MVMCoreUITopAlertBaseView class]]) { - [((MVMCoreUITopAlertBaseView *)view) handleAccessibility]; - } + + [weakSelf updateAccessibilityForTopAlert:view]; [MVMCoreDispatchUtility performBlockInBackground:^{ if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewShown:topAlertObject:)]) { diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index 60ff4872..fed0bb30 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -89,7 +89,11 @@ if (![focusedElement isKindOfClass:[UIView class]]) { return NO; } - return [(UIView *)focusedElement isDescendantOfView:view]; + __block BOOL containsFocus; + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ + containsFocus = [(UIView *)focusedElement isDescendantOfView:view]; + }]; + return containsFocus; } #pragma mark - Setters From d3fd303988860f9fd767fc01a96b462a03eded16 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Sep 2020 09:24:38 -0400 Subject: [PATCH 34/54] comments --- .../Molecules/TopNotification/NotificationXButton.swift | 2 +- MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift index 434ddc3d..84b1abbf 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift @@ -32,7 +32,7 @@ import Foundation guard let model = model as? NotificationXButtonModel else { return } tintColor = model.color.uiColor - // temporary + // TODO: Temporary, consider action for dismissing top alert if model.action.actionType == ActionNoopModel.identifier { addActionBlock(event: .touchUpInside) { (button) in (button as? NotificationXButton)?.closeTopAlert() diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 09cef092..8bf4860f 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -61,17 +61,16 @@ public extension MVMCoreUITopAlertView { let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) guard let json = topAlertObject.json else { return nil } let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject) - guard let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil)/*, - let view = molecule as? MVMCoreUITopAlertBaseView*/ else { - throw ModelRegistry.Error.decoderOther(message: "Molecule not a top alert") + guard let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil) else { + throw ModelRegistry.Error.decoderOther(message: "Molecule not mapped") } if let castView = molecule as? StatusBarUI { let (color, style) = castView.getStatusBarUI() statusBarColor?.pointee = color statusBarStyle?.pointee = style } - // Temporary - molecule.heightAnchor.constraint(lessThanOrEqualToConstant: 150).isActive = true + // TODO: Temporary, waiting for actual restriction from design. + molecule.heightAnchor.constraint(lessThanOrEqualToConstant: 140).isActive = true return molecule } catch { if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { From 30924c6c504186171e45c90b316db4431d3a1ee3 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Sep 2020 09:56:31 -0400 Subject: [PATCH 35/54] review comment --- .../TopNotification/CollapsableNotification.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index 6c8d84b8..f5181511 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -81,11 +81,11 @@ import Foundation let delay: DispatchTimeInterval = DispatchTimeInterval.seconds((model as? CollapsableNotificationModel)?.collapseTime ?? 5) DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in // If accessibility focused, delay collapse. - guard let localSelf = self else { return } - if MVMCoreUIUtility.viewContainsAccessiblityFocus(localSelf) { - NotificationCenter.default.addObserver(localSelf, selector: #selector(localSelf.accessibilityFocusChanged(notification:)), name: UIAccessibility.elementFocusedNotification, object: nil) + guard let self = self else { return } + if MVMCoreUIUtility.viewContainsAccessiblityFocus(self) { + NotificationCenter.default.addObserver(self, selector: #selector(self.accessibilityFocusChanged(notification:)), name: UIAccessibility.elementFocusedNotification, object: nil) } else { - localSelf.collapse() + self.collapse() } } } @@ -150,7 +150,7 @@ import Foundation } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 96 + return 120 } /// Collapse if focus is no longer on this top alert. From f6c4609deb58214663792572a0b596cd31dd2d9d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Sep 2020 09:57:40 -0400 Subject: [PATCH 36/54] comments --- MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift index c7d869c2..d8d4fc08 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift @@ -63,6 +63,7 @@ import Foundation } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + // Currently hardcoded to 96 above. return 96 } From 961625caf3eb792a04ce74095c0d6be3369d91ed Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Sep 2020 10:16:33 -0400 Subject: [PATCH 37/54] review update --- .../Atomic/Molecules/TopNotification/Notification.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift index d8d4fc08..44ce254b 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift @@ -20,6 +20,9 @@ import Foundation public var labelStack: Stack! public var horizontalStack: Stack! + // Legacy constant + private static let viewHeight: CGFloat = 96.0 + //-------------------------------------------------- // MARK: - Life Cycle //-------------------------------------------------- @@ -35,7 +38,7 @@ import Foundation labelStack.restack() horizontalStack.restack() - heightAnchor.constraint(equalToConstant: 96).isActive = true + heightAnchor.constraint(equalToConstant: Self.viewHeight).isActive = true } open override func updateView(_ size: CGFloat) { @@ -63,8 +66,7 @@ import Foundation } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - // Currently hardcoded to 96 above. - return 96 + return viewHeight } open func updateAccessibility() { From 3e2fd50e171d60fdc6f27b04b43a4948db62b329 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Sep 2020 13:05:40 -0400 Subject: [PATCH 38/54] tag showing the alert --- MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h | 4 ++++ MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m | 3 +++ MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m | 1 + 3 files changed, 8 insertions(+) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h index 14da21ad..b61c2b0a 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h @@ -8,6 +8,7 @@ @import MVMCore.MVMCoreLoggingHandler; @class MFViewController; +@class MVMCoreTopAlertObject; NS_ASSUME_NONNULL_BEGIN @@ -20,6 +21,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)defaultLogActionForController:(nullable id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; - (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nullable id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; +// Logging top notification. +- (void)trackTopNotificationShown:(nonnull UIView *)topNotification topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject additionalData:(nullable NSDictionary *)additionalData; + @end NS_ASSUME_NONNULL_END diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m index 45a13acf..ed073722 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m @@ -20,4 +20,7 @@ return nil; } +- (void)trackTopNotificationShown:(UIView *)topNotification topAlertObject:(MVMCoreTopAlertObject *)topAlertObject additionalData:(NSDictionary *)additionalData { +} + @end diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 31899d3f..d54190b0 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -227,6 +227,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewShown:topAlertObject:)]) { [weakSelf.topAlertObject.delegate topAlertViewShown:view topAlertObject:topAlertObject]; } + [[MVMCoreUILoggingHandler sharedLoggingHandler] trackTopNotificationShown:view topAlertObject:topAlertObject additionalData:nil]; [operation markAsFinished]; completionHandler(finished); }]; From cac7180be50060e9d676c84d7efe92ddc0bded1d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Sep 2020 10:35:22 -0400 Subject: [PATCH 39/54] notification bug fixes --- .../TopNotification/CollapsableNotification.swift | 14 +++++++++----- .../CollapsableNotificationTopView.swift | 7 +++++++ .../Molecules/TopNotification/Notification.swift | 10 ++++++++++ .../TopNotification/NotificationXButton.swift | 1 + 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index f5181511..53585b74 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -53,12 +53,16 @@ import Foundation open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? CollapsableNotificationModel else { return } - - topView.label.set(with: model.topLabel, delegateObject, additionalData) - topView.button.set(with: model.topAction, delegateObject: delegateObject, additionalData: additionalData) - topView.updateAccessibility() - + topView.set(with: model, delegateObject, additionalData) bottomView.set(with: model, delegateObject, additionalData) + + // Update top view default noop to expand. + if model.topAction?.actionType == ActionNoopModel.identifier { + topView.button.addActionBlock(event: .touchUpInside) { [weak self] (button) in + Button.performButtonAction(with: model.topAction!, button: button, delegateObject: delegateObject, additionalData: additionalData) + self?.expand(topViewShowing: model.alwaysShowTopLabel) + } + } // Set initial collapse/expand state. topView.isHidden = !model.alwaysShowTopLabel && !model.initiallyCollapsed diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift index 36ddc8a6..c23edeae 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift @@ -43,6 +43,13 @@ import Foundation label.updateView(size) } + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + guard let model = model as? CollapsableNotificationModel else { return } + label.set(with: model.topLabel, delegateObject, additionalData) + button.set(with: model.topAction, delegateObject: delegateObject, additionalData: additionalData) + updateAccessibility() + } + open override func reset() { super.reset() label.setFontStyle(.BoldBodySmall) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift index 44ce254b..09edbbcc 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/Notification.swift @@ -31,6 +31,16 @@ import Foundation super.setupView() reset() + // Buttons should have highest priority, then headline, then body + headline.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 500), for: .horizontal) + headline.setContentHuggingPriority(.required, for: .vertical) + body.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 500), for: .horizontal) + body.setContentHuggingPriority(.required, for: .vertical) + headline.setContentCompressionResistancePriority(UILayoutPriority(rawValue: body.contentCompressionResistancePriority(for: .vertical).rawValue + 40), for: .vertical) + headline.lineBreakMode = .byTruncatingTail + body.lineBreakMode = .byTruncatingTail + button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + labelStack = Stack.createStack(with: [headline, body], spacing: 0) horizontalStack = Stack.createStack(with: [(view: labelStack, model: StackItemModel()),(view: button, model: StackItemModel(horizontalAlignment: .fill)),(view: closeButton, model: StackItemModel(horizontalAlignment: .fill))], axis: .horizontal) addSubview(horizontalStack) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift index 84b1abbf..65c6e18c 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift @@ -25,6 +25,7 @@ import Foundation tintColor = .white adjustsImageWhenHighlighted = false accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "AccCloseButton") + setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { From 2984a1e05c5cdca01e16b25f9d737f5aa717c0c0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Sep 2020 10:50:37 -0400 Subject: [PATCH 40/54] comment --- .../Molecules/TopNotification/CollapsableNotification.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index 53585b74..8539c817 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -57,9 +57,10 @@ import Foundation bottomView.set(with: model, delegateObject, additionalData) // Update top view default noop to expand. - if model.topAction?.actionType == ActionNoopModel.identifier { + if let topAction = model.topAction, + topAction.actionType == ActionNoopModel.identifier { topView.button.addActionBlock(event: .touchUpInside) { [weak self] (button) in - Button.performButtonAction(with: model.topAction!, button: button, delegateObject: delegateObject, additionalData: additionalData) + Button.performButtonAction(with: topAction, button: button, delegateObject: delegateObject, additionalData: additionalData) self?.expand(topViewShowing: model.alwaysShowTopLabel) } } From e05d3e3e1ae1cf03b6a2e70551e05140c41c1c6a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Sep 2020 14:45:26 -0400 Subject: [PATCH 41/54] fix to legacy back button --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++++---- ...ocol.swift => NavigationButtonModelProtocol.swift} | 0 .../Molecules/NavigationBar/NavigationItemModel.swift | 8 +++----- .../ModelProtocols/NavigationItemModelProtocol.swift | 1 + MVMCoreUI/Containers/NavigationController.swift | 2 +- .../MVMCoreUISplitViewController+Extension.swift | 11 +++++------ .../MVMCoreUISplitViewController.m | 1 - 7 files changed, 14 insertions(+), 17 deletions(-) rename MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/{NavigationButtomModelProtocol.swift => NavigationButtonModelProtocol.swift} (100%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 404ae487..72326c6c 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -378,7 +378,7 @@ D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */; }; D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */; }; D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */; }; - D28BA730247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA72F247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift */; }; + D28BA730247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA72F247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift */; }; D28BA741248025A300B75CB8 /* TabBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA740248025A300B75CB8 /* TabBarModel.swift */; }; D28BA7432480284E00B75CB8 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA7422480284E00B75CB8 /* TabBar.swift */; }; D28BA7452481652D00B75CB8 /* TabBarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */; }; @@ -869,7 +869,7 @@ D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonViewModel.swift; sourceTree = ""; }; D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabelsModel.swift; sourceTree = ""; }; D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyCaretLinkImageModel.swift; sourceTree = ""; }; - D28BA72F247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtomModelProtocol.swift; sourceTree = ""; }; + D28BA72F247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonModelProtocol.swift; sourceTree = ""; }; D28BA740248025A300B75CB8 /* TabBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarModel.swift; sourceTree = ""; }; D28BA7422480284E00B75CB8 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = ""; }; D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarProtocol.swift; sourceTree = ""; }; @@ -1525,7 +1525,7 @@ D23EA7FC247EBB7500D60C34 /* Buttons */ = { isa = PBXGroup; children = ( - D28BA72F247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift */, + D28BA72F247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift */, D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */, D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */, D23EA7FD247EBBB700D60C34 /* NavigationLabelButtonModel.swift */, @@ -2522,7 +2522,7 @@ 323AC96A24C837F000F8E4C4 /* ListThreeColumnBillChangesModel.swift in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, - D28BA730247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift in Sources */, + D28BA730247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift in Sources */, 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */, AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationButtomModelProtocol.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationButtonModelProtocol.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationButtomModelProtocol.swift rename to MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationButtonModelProtocol.swift diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index 4ba785f3..daf6c013 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -19,7 +19,7 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt public var tintColor: Color public var line: LineModel? public var alwaysShowBackButton: Bool? - public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? = NavigationImageButtonModel(with: "nav_back", action: ActionBackModel()) + public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? public var titleView: MoleculeModelProtocol? @@ -54,10 +54,8 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard) - alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) - if let backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol) = try typeContainer.decodeModelIfPresent(codingKey: .backButton) { - self.backButton = backButton - } + alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) ?? false + backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton) additionalLeftButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalLeftButtons) additionalRightButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalRightButtons) titleView = try typeContainer.decodeModelIfPresent(codingKey: .titleView) diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift index 38d95555..8f82c946 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift @@ -14,6 +14,7 @@ public protocol NavigationItemModelProtocol { var backgroundColor: Color? { get set } var tintColor: Color { get set } var line: LineModel? { get set } + var hidesSystemBackButton: Bool { get set } var alwaysShowBackButton: Bool? { get set } var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? { get set } var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? { get set } diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 74f24957..22864027 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -47,7 +47,7 @@ import UIKit public static func setNavigationItem(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title - viewController.navigationItem.hidesBackButton = (navigationItemModel.backButton != nil) + viewController.navigationItem.hidesBackButton = navigationItemModel.hidesSystemBackButton setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index 24a8d9f7..515166c6 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -44,13 +44,12 @@ public extension MVMCoreUISplitViewController { let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject // Add back button first. - if navigationItemModel?.alwaysShowBackButton != false { + if navigationItemModel?.alwaysShowBackButton != false, + navigationController.viewControllers.count > 1 || navigationItemModel!.alwaysShowBackButton ?? false { if let backButtonModel = navigationItemModel?.backButton { - if navigationController.viewControllers.count > 1 || navigationItemModel!.alwaysShowBackButton ?? false { - leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) - } - } else if let backButton = backButton, - navigationController.viewControllers.count > 1 { + leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } else if let backButton = backButton { + // Default to legacy if we have default back button. leftItems.append(backButton) } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 288878b8..177d0b8a 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -923,7 +923,6 @@ CGFloat const PanelAnimationDuration = 0.2; // Creates the back button self.backButton = [[UIBarButtonItem alloc] initWithImage:[self imageForBackButton] style:UIBarButtonItemStylePlain target:self action:@selector(backButtonPressed:)]; - self.backButton.imageInsets = UIEdgeInsetsMake(0, 4, 0, -8); // Dismisses a panel if the user taps the main view. if (!self.tapToDismissGesture) { From b4cec30ab0920eec0f21c05169bc9cb3eec3d6f7 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Sep 2020 14:53:18 -0400 Subject: [PATCH 42/54] remove debug code --- .../Atomic/Molecules/NavigationBar/NavigationItemModel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index daf6c013..283da64d 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -18,6 +18,7 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt public var backgroundColor: Color? public var tintColor: Color public var line: LineModel? + public var hidesSystemBackButton = true public var alwaysShowBackButton: Bool? public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? @@ -54,7 +55,7 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard) - alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) ?? false + alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton) additionalLeftButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalLeftButtons) additionalRightButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalRightButtons) From c3040c24d3c09d6fd790ddc0f0daf22c8ec74702 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Sep 2020 16:26:55 -0400 Subject: [PATCH 43/54] code review --- .../MVMCoreUISplitViewController+Extension.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index 515166c6..d37d88f1 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -44,8 +44,13 @@ public extension MVMCoreUISplitViewController { let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject // Add back button first. - if navigationItemModel?.alwaysShowBackButton != false, - navigationController.viewControllers.count > 1 || navigationItemModel!.alwaysShowBackButton ?? false { + var showBackButton: Bool + if let forceBackButton = navigationItemModel?.alwaysShowBackButton { + showBackButton = forceBackButton + } else { + showBackButton = navigationController.viewControllers.count > 1 + } + if showBackButton { if let backButtonModel = navigationItemModel?.backButton { leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) } else if let backButton = backButton { From ddd30a0f26789e801ffa237b85cce5e82a41dc52 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Sep 2020 16:29:25 -0400 Subject: [PATCH 44/54] comment --- .../Atomic/Molecules/NavigationBar/NavigationItemModel.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index 283da64d..d8c8c7ac 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -19,8 +19,11 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt public var tintColor: Color public var line: LineModel? public var hidesSystemBackButton = true + + /// If true, we add the button in the backButton property. If false we do not add the button. If nil, we add the button if the controller is not the bottom of the stack public var alwaysShowBackButton: Bool? public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? + public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? public var titleView: MoleculeModelProtocol? From fa99ba6b42e8047491300caf41c7133101eb9459 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 29 Sep 2020 12:01:12 -0400 Subject: [PATCH 45/54] Improving accordion behavior. Not the +/- button responds to user interaction. --- .../AccordionMoleculeTableViewCell.swift | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift index 8488703d..07bd9049 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift @@ -6,8 +6,6 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import UIKit - @objcMembers public class AccordionMoleculeTableViewCell: MoleculeTableViewCell { //-------------------------------------------------- @@ -28,6 +26,8 @@ import UIKit return accordionButton }() + var delegateObject: MVMCoreUIDelegateObject? + //-------------------------------------------------- // MARK: - Setup //-------------------------------------------------- @@ -36,10 +36,27 @@ import UIKit customAccessoryView = true super.setupView() accessoryView = accordionButton + + accordionButton.addActionBlock(event: .touchUpInside) { _ in + guard let model = self.accordionListItemModel else { return } + + if let indexPath = self.delegateObject?.moleculeDelegate?.getIndexPath(for: model) { + self.toggleAccordion(at: indexPath, delegateObject: self.delegateObject) + } + } } + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + override public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + toggleAccordion(at: index, delegateObject: delegateObject) + } + + func toggleAccordion(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?) { + accordionButton.isSelected.toggle() accordionButton.setTitle(accordionButton.isSelected ? "-" : "+", for: .normal) @@ -57,4 +74,9 @@ import UIKit bottomSeparatorView?.isHidden = accordionButton.isSelected } } + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + self.delegateObject = delegateObject + } } From 5398008a0dc7783f6f750c0d0e38f1dea45768f0 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 29 Sep 2020 13:34:32 -0400 Subject: [PATCH 46/54] rollback some behavior --- .../AccordionMoleculeTableViewCell.swift | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift index 07bd9049..f7a493d7 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift @@ -26,8 +26,6 @@ return accordionButton }() - var delegateObject: MVMCoreUIDelegateObject? - //-------------------------------------------------- // MARK: - Setup //-------------------------------------------------- @@ -36,27 +34,11 @@ customAccessoryView = true super.setupView() accessoryView = accordionButton - - accordionButton.addActionBlock(event: .touchUpInside) { _ in - guard let model = self.accordionListItemModel else { return } - - if let indexPath = self.delegateObject?.moleculeDelegate?.getIndexPath(for: model) { - self.toggleAccordion(at: indexPath, delegateObject: self.delegateObject) - } - } + accessoryView?.isUserInteractionEnabled = false } - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - override public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - toggleAccordion(at: index, delegateObject: delegateObject) - } - - func toggleAccordion(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?) { - accordionButton.isSelected.toggle() accordionButton.setTitle(accordionButton.isSelected ? "-" : "+", for: .normal) @@ -74,9 +56,4 @@ bottomSeparatorView?.isHidden = accordionButton.isSelected } } - - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - super.set(with: model, delegateObject, additionalData) - self.delegateObject = delegateObject - } } From 108eca30dbb60796354259afc4a910cff34f58c5 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 30 Sep 2020 15:22:39 -0400 Subject: [PATCH 47/54] iOS 13/14 issue fix. --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 748013f0..87939c52 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -247,6 +247,7 @@ public typealias ActionBlock = () -> () return } + text = nil attributedText = nil originalAttributedString = nil text = labelModel.text @@ -365,6 +366,20 @@ public typealias ActionBlock = () -> () attributedText = attributedString originalAttributedString = attributedText } + + /* + * This is to address a reuse issue beginning with iOS 13 (also present in iOS 14). + * Even if you set text & attributedText to nil, the moment you set text with a value, + * attributedText will hold dirty value from a previously reused cell. + */ + if #available(iOS 13, *) { + if labelModel.attributes == nil, let text = text { + let attributedString = NSMutableAttributedString(string: text, + attributes: [NSAttributedString.Key.font: font.updateSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor]) + attributedString.addAttribute(.underlineStyle, value: 0, range: NSRange(location: 0, length: text.count)) + attributedText = attributedString + } + } } @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { From 84a300eba73dacbfeabc5382b18c2001afbdaf80 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 30 Sep 2020 15:54:51 -0400 Subject: [PATCH 48/54] checked & comment --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 87939c52..fe6b31eb 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -368,14 +368,16 @@ public typealias ActionBlock = () -> () } /* - * This is to address a reuse issue beginning with iOS 13 (also present in iOS 14). + * This is to address a reuse issue with iOS 13 and up. * Even if you set text & attributedText to nil, the moment you set text with a value, - * attributedText will hold dirty value from a previously reused cell. + * attributedText will hold a dirty value from a previously reused cell even if reset() is + * appropriately called. + * Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml */ if #available(iOS 13, *) { - if labelModel.attributes == nil, let text = text { - let attributedString = NSMutableAttributedString(string: text, - attributes: [NSAttributedString.Key.font: font.updateSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor]) + // Case where no attributes are set in the model but attributedText has a value. + if labelModel.attributes == nil && attributedText != nil, let text = text { + let attributedString = NSMutableAttributedString(string: text) attributedString.addAttribute(.underlineStyle, value: 0, range: NSRange(location: 0, length: text.count)) attributedText = attributedString } From 592e0a9591b114af0b81f017f1ecdf097017e172 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 1 Oct 2020 09:03:48 -0400 Subject: [PATCH 49/54] move logic up --- .../Atomic/Atoms/Views/Label/Label.swift | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index fe6b31eb..a5bf5f5e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -251,6 +251,23 @@ public typealias ActionBlock = () -> () attributedText = nil originalAttributedString = nil text = labelModel.text + + /* + * This is to address a reuse issue with iOS 13 and up. + * Even if you set text & attributedText to nil, the moment you set text with a value, + * attributedText will hold a dirty value from a previously reused cell even if reset() is + * appropriately called. + * Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml + */ + if #available(iOS 13, *) { + // Case where no attributes are set in the model but attributedText has a value. + if attributedText != nil, let text = text { + let attributedString = NSMutableAttributedString(string: text) + attributedString.addAttribute(.underlineStyle, value: 0, range: NSRange(location: 0, length: text.count)) + attributedText = attributedString + } + } + hero = labelModel.hero Label.setLabel(self, withHTML: labelModel.html) isAccessibilityElement = hasText @@ -366,22 +383,6 @@ public typealias ActionBlock = () -> () attributedText = attributedString originalAttributedString = attributedText } - - /* - * This is to address a reuse issue with iOS 13 and up. - * Even if you set text & attributedText to nil, the moment you set text with a value, - * attributedText will hold a dirty value from a previously reused cell even if reset() is - * appropriately called. - * Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml - */ - if #available(iOS 13, *) { - // Case where no attributes are set in the model but attributedText has a value. - if labelModel.attributes == nil && attributedText != nil, let text = text { - let attributedString = NSMutableAttributedString(string: text) - attributedString.addAttribute(.underlineStyle, value: 0, range: NSRange(location: 0, length: text.count)) - attributedText = attributedString - } - } } @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { From 0ba5da2add4d78b6f740eea873dbbae828020585 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 1 Oct 2020 12:15:45 -0400 Subject: [PATCH 50/54] latest state --- .../Atomic/Atoms/Views/Label/Label.swift | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index a5bf5f5e..62af450d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -252,6 +252,8 @@ public typealias ActionBlock = () -> () originalAttributedString = nil text = labelModel.text + print("SET before:\n \(attributedText)") + /* * This is to address a reuse issue with iOS 13 and up. * Even if you set text & attributedText to nil, the moment you set text with a value, @@ -261,13 +263,23 @@ public typealias ActionBlock = () -> () */ if #available(iOS 13, *) { // Case where no attributes are set in the model but attributedText has a value. - if attributedText != nil, let text = text { + if let attributedText = attributedText, let text = text { let attributedString = NSMutableAttributedString(string: text) - attributedString.addAttribute(.underlineStyle, value: 0, range: NSRange(location: 0, length: text.count)) - attributedText = attributedString + for attributes in attributedText.attributes(at: 0, effectiveRange: nil) { + if attributes.key == .underlineStyle { + attributedString.addAttribute(.underlineStyle, value: 0, range: NSRange(location: 0, length: text.count)) + } + if attributes.key == .strikethroughStyle { + attributedString.addAttribute(.strikethroughStyle, value: 0, range: NSRange(location: 0, length: text.count)) + } + } + + self.attributedText = attributedString } } + print("SET after:\n \(attributedText)") + hero = labelModel.hero Label.setLabel(self, withHTML: labelModel.html) isAccessibilityElement = hasText @@ -382,7 +394,9 @@ public typealias ActionBlock = () -> () attributedText = attributedString originalAttributedString = attributedText + print("SET new attribute:\n \(attributedText)") } + print("SET DONE FUNC:\n \(attributedText)") } @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { @@ -773,6 +787,7 @@ extension Label { public func reset() { text = nil attributedText = nil + print("reset:\n \(attributedText)") hero = nil textAlignment = .left originalAttributedString = nil From 5bac63324ff0e34d3a8d9b6e5afc6b1a302d42d6 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 1 Oct 2020 14:45:18 -0400 Subject: [PATCH 51/54] update --- .../Atomic/Atoms/Views/Label/Label.swift | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 62af450d..bea0dfea 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -252,8 +252,6 @@ public typealias ActionBlock = () -> () originalAttributedString = nil text = labelModel.text - print("SET before:\n \(attributedText)") - /* * This is to address a reuse issue with iOS 13 and up. * Even if you set text & attributedText to nil, the moment you set text with a value, @@ -262,15 +260,15 @@ public typealias ActionBlock = () -> () * Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml */ if #available(iOS 13, *) { - // Case where no attributes are set in the model but attributedText has a value. if let attributedText = attributedText, let text = text { let attributedString = NSMutableAttributedString(string: text) - for attributes in attributedText.attributes(at: 0, effectiveRange: nil) { - if attributes.key == .underlineStyle { - attributedString.addAttribute(.underlineStyle, value: 0, range: NSRange(location: 0, length: text.count)) + let range = NSRange(location: 0, length: text.count) + for attribute in attributedText.attributes(at: 0, effectiveRange: nil) { + if attribute.key == .underlineStyle { + attributedString.addAttribute(.underlineStyle, value: 0, range: range) } - if attributes.key == .strikethroughStyle { - attributedString.addAttribute(.strikethroughStyle, value: 0, range: NSRange(location: 0, length: text.count)) + if attribute.key == .strikethroughStyle { + attributedString.addAttribute(.strikethroughStyle, value: 0, range: range) } } @@ -278,8 +276,6 @@ public typealias ActionBlock = () -> () } } - print("SET after:\n \(attributedText)") - hero = labelModel.hero Label.setLabel(self, withHTML: labelModel.html) isAccessibilityElement = hasText @@ -394,9 +390,7 @@ public typealias ActionBlock = () -> () attributedText = attributedString originalAttributedString = attributedText - print("SET new attribute:\n \(attributedText)") } - print("SET DONE FUNC:\n \(attributedText)") } @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { @@ -787,7 +781,6 @@ extension Label { public func reset() { text = nil attributedText = nil - print("reset:\n \(attributedText)") hero = nil textAlignment = .left originalAttributedString = nil From 40f2b5db172f55f9fccdad2d204e8739f48e1f4c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 2 Oct 2020 12:00:09 -0400 Subject: [PATCH 52/54] moved logic --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index bea0dfea..d8e53a30 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -241,15 +241,15 @@ public typealias ActionBlock = () -> () public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { clauses = [] + text = nil + attributedText = nil + originalAttributedString = nil guard let labelModel = model as? LabelModel else { text = "" return } - text = nil - attributedText = nil - originalAttributedString = nil text = labelModel.text /* From e676e1f7c1df21b9da8222ea8d2f419187af6c9d Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 2 Oct 2020 12:17:51 -0400 Subject: [PATCH 53/54] not needed --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index d8e53a30..15cf5a22 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -245,10 +245,7 @@ public typealias ActionBlock = () -> () attributedText = nil originalAttributedString = nil - guard let labelModel = model as? LabelModel else { - text = "" - return - } + guard let labelModel = model as? LabelModel else { return } text = labelModel.text From 7c636a00b1ac34a6886480cd73aa6b774f3a1d60 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 6 Oct 2020 10:30:37 -0400 Subject: [PATCH 54/54] check length --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 15cf5a22..c9f676a8 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -257,7 +257,7 @@ public typealias ActionBlock = () -> () * Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml */ if #available(iOS 13, *) { - if let attributedText = attributedText, let text = text { + if let attributedText = attributedText, let text = text, !text.isEmpty { let attributedString = NSMutableAttributedString(string: text) let range = NSRange(location: 0, length: text.count) for attribute in attributedText.attributes(at: 0, effectiveRange: nil) {