Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui into feature/action_modernize

This commit is contained in:
Scott Pfeil 2022-07-18 19:01:01 -04:00
commit 4f2fa7962a
31 changed files with 191 additions and 149 deletions

View File

@ -129,6 +129,8 @@
187FEB2A2844D2A600BF29C2 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 187FEB292844D2A600BF29C2 /* VDSFormControlsTokens.xcframework */; };
1D6D258826899B0C00DEBB08 /* ImageButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D6D258626899B0B00DEBB08 /* ImageButtonModel.swift */; };
1D6D258926899B0C00DEBB08 /* ImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D6D258726899B0B00DEBB08 /* ImageButton.swift */; };
27559EFC27D691D3000836C1 /* ViewMaskingProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27559EFB27D691D3000836C1 /* ViewMaskingProtocol.swift */; };
27577DCD286CA959001EC47E /* MoleculeMaskingProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27577DCC286CA959001EC47E /* MoleculeMaskingProtocol.swift */; };
279B1569242BBC2F00921D6C /* ActionModelAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */; };
27F6B08826051831008529AA /* MoleculeTreeTraversalProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F6B08726051831008529AA /* MoleculeTreeTraversalProtocol.swift */; };
27F6B08C26052AFF008529AA /* ParentMoleculeModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F6B08B26052AFF008529AA /* ParentMoleculeModelProtocol.swift */; };
@ -727,6 +729,8 @@
187FEB292844D2A600BF29C2 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = "<group>"; };
1D6D258626899B0B00DEBB08 /* ImageButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageButtonModel.swift; path = MVMCoreUI/Atomic/Atoms/Buttons/ImageButtonModel.swift; sourceTree = SOURCE_ROOT; };
1D6D258726899B0B00DEBB08 /* ImageButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageButton.swift; path = MVMCoreUI/Atomic/Atoms/Buttons/ImageButton.swift; sourceTree = SOURCE_ROOT; };
27559EFB27D691D3000836C1 /* ViewMaskingProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewMaskingProtocol.swift; sourceTree = "<group>"; };
27577DCC286CA959001EC47E /* MoleculeMaskingProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeMaskingProtocol.swift; sourceTree = "<group>"; };
279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionModelAdapter.swift; sourceTree = "<group>"; };
27F6B08726051831008529AA /* MoleculeTreeTraversalProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTreeTraversalProtocol.swift; sourceTree = "<group>"; };
27F6B08B26052AFF008529AA /* ParentMoleculeModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentMoleculeModelProtocol.swift; sourceTree = "<group>"; };
@ -1234,6 +1238,7 @@
D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */,
D28BA74C248589C800B75CB8 /* TabPageModelProtocol.swift */,
27F6B08B26052AFF008529AA /* ParentMoleculeModelProtocol.swift */,
27577DCC286CA959001EC47E /* MoleculeMaskingProtocol.swift */,
);
path = ModelProtocols;
sourceTree = "<group>";
@ -2443,6 +2448,7 @@
D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */,
D2B9D0E3265EEE9D0084735C /* MoleculeListProtocol.swift */,
011B58EE23A2AA850085F53C /* ModelProtocols */,
27559EFB27D691D3000836C1 /* ViewMaskingProtocol.swift */,
);
path = Protocols;
sourceTree = "<group>";
@ -2915,6 +2921,7 @@
D23EA800247EBD6C00D60C34 /* LabelBarButtonItem.swift in Sources */,
01EB368F23609801006832FA /* LabelModel.swift in Sources */,
0A6682AC243531C300AD3CA1 /* Padding.swift in Sources */,
27559EFC27D691D3000836C1 /* ViewMaskingProtocol.swift in Sources */,
AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */,
AA37CBD52519072F0027344C /* Stars.swift in Sources */,
942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */,
@ -3095,6 +3102,7 @@
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,
EA41F4AC2787927100F5B377 /* DynamicRuleFormFieldEffectModel.swift in Sources */,
011D95892404249B000E3791 /* FormHolderModelProtocol.swift in Sources */,
27577DCD286CA959001EC47E /* MoleculeMaskingProtocol.swift in Sources */,
BB54C5202434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift in Sources */,
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */,
013F801923FB4A8E00AD8013 /* UIContentMode+Extension.swift in Sources */,

View File

@ -62,11 +62,4 @@ typedef void (^TextFieldErrorHandler)(NSArray * _Nonnull fieldErrors);
// Will show this alert in it's appropriate type style.
- (void)showAlert;
#pragma mark - Deprecated
// Creates an alert object for an error with the passed in load object response info
+ (nullable instancetype)alertObjectForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error actionDelegate:(nullable NSObject <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)actionDelegate __deprecated;
+ (nullable instancetype)alertObjectForPageType:(nullable NSString *)pageType responseInfo:(nullable NSDictionary *)responseInfo additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)actionDelegate __deprecated;
+ (nullable instancetype)alertObjectWithPage:(nullable NSDictionary *)page isGreedy:(BOOL)isGreedy additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate error:(MVMCoreErrorObject *_Nullable *_Nullable)error __deprecated;
@end

View File

@ -195,113 +195,4 @@
}
}
#pragma mark - Deprecated
+ (nullable instancetype)alertObjectForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error actionDelegate:(nullable NSObject <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)actionDelegate {
MVMCoreAlertObject *alert = nil;
if (!error || [ErrorDomainServer isEqualToString:error.domain]) {
alert = [MVMCoreAlertObject alertObjectForPageType:loadObject.pageType responseInfo:loadObject.responseInfoMap additionalData:loadObject.dataForPage actionDelegate:actionDelegate];
} else {
alert = [[MVMCoreAlertObject alloc] initPopupAlertWithError:error isGreedy:NO];
}
// only if actions are empty, then go inside and set OK as default action
if (alert.type == MFAlertTypePopup && alert.actions.count == 0) {
alert.defaultAction = YES;
alert.actions = @[[UIAlertAction actionWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedOK] style:UIAlertActionStyleDefault handler:nil]];
}
return alert;
}
+ (nullable instancetype)alertObjectForPageType:(nullable NSString *)pageType responseInfo:(nullable NSDictionary *)responseInfo additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)actionDelegate {
__block MVMCoreAlertObject *alert = [[MVMCoreAlertObject alloc] init];
alert.title = [responseInfo string:KeyErrorHeading] ?: [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle];
alert.message = [responseInfo string:KeyUserMessage] ?: [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess];
NSString *messageStyle = [responseInfo stringForKey:KeyMessageStyle];
if ([ValueTypeFieldErrors isEqualToString:[responseInfo string:KeyType]]) {
// field errors.
alert.type = MFAlertTypeField;
alert.fieldErrors = [responseInfo array:ValueTypeFieldErrors];
} else {
// Check for top alert (persistent or regular).
if ([messageStyle isEqualToString:ValueMessageStyleTopPersistent] || [messageStyle isEqualToString:ValueMessageStyleTop]) {
alert.topAlertObject = [[MVMCoreTopAlertObject alloc] initWithResponseInfo:responseInfo];
if ([actionDelegate conformsToProtocol:@protocol(MVMCoreTopAlertDelegateProtocol)]) {
alert.topAlertObject.delegate = (NSObject <MVMCoreTopAlertDelegateProtocol> *)actionDelegate;
}
alert.topAlertObject.pageType = pageType;
alert.type = MFAlertTypeTop;
} else if ([messageStyle isEqualToString:ValueMessageStylePopup]) {
// Perform a popup.
alert.type = MFAlertTypePopup;
alert.alertStyle = UIAlertControllerStyleAlert;
// Check if we have a popup driven by page object (otherwise by default it will just use response info title message with an OK button).
NSString *pageTypeForPopup = [responseInfo stringForKey:@"popupPageType"];
[[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForPopup queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
MVMCoreErrorObject *error = nil;
MVMCoreAlertObject *popupAlert = [MVMCoreAlertObject alertObjectWithPage:jsonDictionary isGreedy:NO additionalData:additionalData delegate:actionDelegate error:&error];
if (error) {
// Error, popup page not found for page type.
popupAlert = [[MVMCoreAlertObject alloc] initPopupAlertWithError:error isGreedy:NO];
}
if (popupAlert) {
alert = popupAlert;
}
}];
} else if (messageStyle.length == 0 && pageType) {
// No message style!
alert.type = MFAlertTypeNone;
} else {
// Default to popup
alert.type = MFAlertTypePopup;
alert.alertStyle = UIAlertControllerStyleAlert;
}
}
if ([actionDelegate conformsToProtocol:@protocol(MVMCoreAlertDelegateProtocol)]) {
alert.alertDelegate = (NSObject <MVMCoreAlertDelegateProtocol> *)actionDelegate;
}
return alert;
}
+ (nullable instancetype)alertObjectWithPage:(nullable NSDictionary *)page isGreedy:(BOOL)isGreedy additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate error:(MVMCoreErrorObject *_Nullable *_Nullable)error {
MVMCoreAlertObject *alert = [[MVMCoreAlertObject alloc] init];
alert.title = [page string:KeyTitle] ?: [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle];
alert.pageJson = page;
alert.message = [page string:KeyMessage] ?: [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess];
alert.isGreedy = isGreedy;
alert.type = MFAlertTypePopup;
alert.alertStyle = UIAlertControllerStyleAlert;
NSArray <NSDictionary *> *actions = [page array:KeyLinks];
NSMutableArray <UIAlertAction *> *actionsForAlert = [NSMutableArray array];
for (NSDictionary *actionMap in actions) {
[actionsForAlert addObject:[UIAlertAction actionWithTitle:[actionMap stringForKey:KeyTitle] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegate:delegate];
}]];
}
alert.actions = actionsForAlert;
if ((alert.title.length > 0 || alert.message.length > 0) && alert.actions.count > 0) {
return alert;
} else {
if (error) {
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil messageToLog:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodePopupFailed domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_Popup_pageType:%@",NSStringFromClass([delegate class]),[page stringForKey:KeyPageType]]];
}
return nil;
}
}
@end

View File

@ -32,6 +32,8 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
public var inverted = false
public var size:linkFontSize = linkFontSize.small
public var shouldMaskRecordedView: Bool? = false
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
@ -61,6 +63,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
case activeColor_inverted
case inverted
case size
case shouldMaskRecordedView
}
public enum linkFontSize: String, Codable {
@ -117,7 +120,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
if let disabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor_inverted) {
self.disabledColor_inverted = disabledColor_inverted
}
if let activeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .activeColor) {
self.activeColor = activeColor
}
@ -128,6 +131,8 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
if let size = try typeContainer.decodeIfPresent(linkFontSize.self, forKey: .size) {
self.size = size
}
shouldMaskRecordedView = try typeContainer.decodeIfPresent(Bool.self, forKey: .shouldMaskRecordedView) ?? shouldMaskRecordedView
}
public func encode(to encoder: Encoder) throws {
@ -146,5 +151,6 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol, EnableableMode
try container.encode(activeColor, forKey: .activeColor)
try container.encode(activeColor_inverted, forKey: .activeColor_inverted)
try container.encodeIfPresent(size, forKey: .size)
try container.encode(shouldMaskRecordedView, forKey: .shouldMaskRecordedView)
}
}

View File

@ -42,7 +42,8 @@ import Foundation
public var wasInitiallySelected: Bool = false
public var title: String?
public var feedback: String?
public var shouldMaskRecordedView: Bool? = true
//used to drive the EntryFieldView UI
public var titleStateLabel: FormLabelModel
public var feedbackStateLabel: FormLabelModel
@ -79,6 +80,7 @@ import Foundation
case fieldKey
case groupName
case required
case shouldMaskRecordedView
}
//--------------------------------------------------
@ -143,6 +145,7 @@ import Foundation
hideBorders = try typeContainer.decodeIfPresent(Bool.self, forKey: .hideBorders) ?? false
baseValue = text
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
shouldMaskRecordedView = try typeContainer.decodeIfPresent(Bool.self, forKey: .shouldMaskRecordedView) ?? shouldMaskRecordedView
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
@ -172,5 +175,6 @@ import Foundation
try container.encode(enabled, forKey: .enabled)
try container.encode(required, forKey: .required)
try container.encode(hideBorders, forKey: .hideBorders)
try container.encode(shouldMaskRecordedView, forKey: .shouldMaskRecordedView)
}
}

View File

@ -337,12 +337,16 @@ import UIKit
text = model.text
placeholder = model.placeholder
textField.shouldMaskWhileRecording = model.shouldMaskRecordedView ?? true
switch model.type {
case .password, .secure:
textField.isSecureTextEntry = true
textField.shouldMaskWhileRecording = true
case .numberSecure:
textField.isSecureTextEntry = true
textField.shouldMaskWhileRecording = true
textField.keyboardType = .numberPad
case .number:

View File

@ -23,6 +23,7 @@ import MVMCore
public var baseValue: AnyHashable?
public var enabled: Bool = true
public var readOnly: Bool = false
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------

View File

@ -26,6 +26,7 @@
public var localBundle: Bundle?
public var cornerRadius: CGFloat?
public var clipsImage: Bool?
public var shouldMaskRecordedView: Bool? = false
//--------------------------------------------------
// MARK: - Initializer
@ -54,5 +55,6 @@
case contentMode
case cornerRadius
case clipsImage
case shouldMaskRecordedView
}
}

View File

@ -12,7 +12,8 @@ import MVMCore
public typealias ActionBlock = () -> ()
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ViewMaskingProtocol {
//------------------------------------------------------
// MARK: - Properties
//------------------------------------------------------
@ -41,6 +42,8 @@ public typealias ActionBlock = () -> ()
NSRange(location: 0, length: text?.count ?? 0)
}
public var shouldMaskWhileRecording: Bool = false
//------------------------------------------------------
// MARK: - Multi-Action Text
//------------------------------------------------------
@ -244,6 +247,7 @@ public typealias ActionBlock = () -> ()
text = nil
attributedText = nil
originalAttributedString = nil
shouldMaskWhileRecording = model.shouldMaskRecordedView ?? false
guard let labelModel = model as? LabelModel else { return }

View File

@ -26,6 +26,7 @@
public var hero: Int?
public var makeWholeViewClickable: Bool?
public var numberOfLines: Int?
public var shouldMaskRecordedView: Bool? = false
//--------------------------------------------------
// MARK: - Keys
@ -46,6 +47,7 @@
case hero
case makeWholeViewClickable
case numberOfLines
case shouldMaskRecordedView
}
enum AttributeTypeKey: String, CodingKey {
@ -79,6 +81,7 @@
hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero)
makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable)
numberOfLines = try typeContainer.decodeIfPresent(Int.self, forKey: .numberOfLines)
shouldMaskRecordedView = try typeContainer.decodeIfPresent(Bool.self, forKey: .shouldMaskRecordedView) ?? false
}
open func encode(to encoder: Encoder) throws {
@ -97,5 +100,6 @@
try container.encodeIfPresent(hero, forKey: .hero)
try container.encodeIfPresent(makeWholeViewClickable, forKey: .makeWholeViewClickable)
try container.encodeIfPresent(numberOfLines, forKey: .numberOfLines)
try container.encodeIfPresent(shouldMaskRecordedView, forKey: .shouldMaskRecordedView)
}
}

View File

@ -22,7 +22,7 @@
public var addSizeConstraintsForAspectRatio = true
public var shouldNotifyDelegateOnUpdate = true
public var shouldNotifyDelegateOnDefaultSizeChange = false
// Allows for a view to hardcode which height to use if there is none in the json.
var imageWidth: CGFloat?
var imageHeight: CGFloat?

View File

@ -41,7 +41,7 @@ public class HeadersH2TinyButtonModel: HeaderModel, MoleculeModelProtocol {
}
super.setDefaults()
button.style = .secondary
button.size = .tiny
button.size = .small
}
//--------------------------------------------------

View File

@ -51,9 +51,7 @@ open class HeaderView: Container {
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let headerModel = headerModel else { return }
if let lineModel = headerModel.line {
line.set(with: lineModel, delegateObject, additionalData)
}
line.setOptional(with: headerModel.line, delegateObject, additionalData)
}
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {

View File

@ -30,9 +30,6 @@
if bottomPadding == nil {
bottomPadding = PaddingDefaultVerticalSpacing
}
if line == nil {
line = LineModel(type: .heavy)
}
}
public override init() {

View File

@ -0,0 +1,17 @@
//
// MaskedMoleculeProtocol.swift
// MVMCoreUI
//
// Created by Kyle on 6/29/22.
// Copyright © 2022 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol MoleculeMaskingProtocol {
var shouldMaskRecordedView: Bool? { get }
}
public extension MoleculeMaskingProtocol {
var shouldMaskRecordedView: Bool? { return false }
}

View File

@ -5,7 +5,7 @@ public enum MolecularError: Swift.Error {
}
public protocol MoleculeModelProtocol: ModelProtocol, AccessibilityModelProtocol, MoleculeTreeTraversalProtocol {
public protocol MoleculeModelProtocol: ModelProtocol, AccessibilityModelProtocol, MoleculeTreeTraversalProtocol, MoleculeMaskingProtocol {
var moleculeName: String { get }
var backgroundColor: Color? { get set }
}

View File

@ -13,4 +13,5 @@ public protocol PageModelProtocol {
var screenHeading: String? { get set }
var backgroundColor: Color? { get set }
var navigationBar: (NavigationItemModelProtocol & MoleculeModelProtocol)? { get set }
var shouldMaskScreenWhileRecording: Bool? { get }
}

View File

@ -41,6 +41,9 @@ public extension TemplateProtocol {
var behaviorHandler = self as? PageBehaviorHandlerProtocol else { return }
behaviorHandlerModel.traverseAndAddRequiredBehaviors()
behaviorHandler.createBehaviors(for: behaviorHandlerModel, delegateObject: delegateObject)
if let viewController = self as? UIViewController {
MVMCoreUISession.sharedGlobal()?.applyGlobalBehaviors(to: viewController)
}
}
func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel {

View File

@ -0,0 +1,15 @@
//
// ViewMaskingProtocol.swift
// MVMCoreUI
//
// Created by Kyle on 3/7/22.
// Copyright © 2022 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol ViewMaskingProtocol: UIView {
var shouldMaskWhileRecording: Bool { get }
}

View File

@ -30,6 +30,8 @@ import Foundation
public var tabBarHidden: Bool = false
public var tabBarIndex: Int?
public var shouldMaskScreenWhileRecording: Bool?
//--------------------------------------------------
// MARK: - Initializer
@ -53,6 +55,7 @@ import Foundation
case navigationBar
case tabBarHidden
case tabBarIndex
case shouldMaskScreenWhileRecording
}
//--------------------------------------------------
@ -77,6 +80,7 @@ import Foundation
self.tabBarHidden = tabBarHidden
}
tabBarIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .tabBarIndex)
shouldMaskScreenWhileRecording = try typeContainer.decodeIfPresent(Bool.self, forKey: .shouldMaskScreenWhileRecording)
}
open func encode(to encoder: Encoder) throws {
@ -89,5 +93,6 @@ import Foundation
try container.encodeModelIfPresent(navigationBar, forKey: .navigationBar)
try container.encode(tabBarHidden, forKey: .tabBarHidden)
try container.encodeIfPresent(tabBarIndex, forKey: .tabBarIndex)
try container.encode(shouldMaskScreenWhileRecording, forKey: .shouldMaskScreenWhileRecording)
}
}

View File

@ -46,7 +46,7 @@
open override func viewForTop() -> UIView? {
guard let headerModel = templateModel?.header,
let molecule = ModelRegistry.createMolecule(headerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: headerModel)
else { return super.viewForTop() }
// Temporary, Default the horizontal padding
@ -59,7 +59,7 @@
override open func viewForBottom() -> UIView? {
guard let footerModel = templateModel?.footer,
let molecule = ModelRegistry.createMolecule(footerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: footerModel)
else { return super.viewForBottom() }
return molecule
@ -124,7 +124,9 @@
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath)
(cell as? MoleculeViewProtocol)?.reset()
(cell as? MoleculeViewProtocol)?.set(with: moleculeInfo.molecule, delegateObjectIVar, nil)
if let molecularCell = cell as? MoleculeViewProtocol {
updateMoleculeView(molecularCell, from: moleculeInfo.molecule)
}
update(cell: cell, size: view.frame.width)
// Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells
cell.layoutIfNeeded()

View File

@ -58,7 +58,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open override func viewForTop() -> UIView {
guard let headerModel = templateModel?.header,
let molecule = ModelRegistry.createMolecule(headerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: headerModel)
else { return super.viewForTop() }
// Temporary, Default the horizontal padding
@ -71,7 +71,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
override open func viewForBottom() -> UIView {
guard let footerModel = templateModel?.footer,
let molecule = ModelRegistry.createMolecule(footerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: footerModel)
else { return super.viewForBottom() }
return molecule
@ -140,7 +140,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
(cell as? MoleculeViewProtocol)?.reset()
(cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath)
(cell as? MoleculeViewProtocol)?.set(with: moleculeInfo.molecule, delegateObjectIVar, nil)
if let moleculeView = cell as? MoleculeViewProtocol {
updateMoleculeView(moleculeView, from: moleculeInfo.molecule)
}
(cell as? MVMCoreViewProtocol)?.updateView(tableView.bounds.width)
// Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells
cell.layoutIfNeeded()

View File

@ -54,7 +54,7 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
open override func viewForTop() -> UIView? {
guard let headerModel = templateModel?.header,
let molecule = ModelRegistry.createMolecule(headerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: headerModel)
else { return nil }
return molecule
@ -73,13 +73,13 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
stackItem.useHorizontalMargins = true
}
stack.set(with: moleculeStackModel, delegateObject() as? MVMCoreUIDelegateObject, nil)
updateMoleculeView(stack, from: moleculeStackModel)
return stack
}
override open func viewForBottom() -> UIView? {
guard let footerModel = templateModel?.footer,
let molecule = ModelRegistry.createMolecule(footerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: footerModel)
else { return nil }
return molecule

View File

@ -27,7 +27,7 @@ import UIKit
open override func viewForTop() -> UIView? {
guard let headerModel = templateModel?.header,
let molecule = ModelRegistry.createMolecule(headerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: headerModel)
else { return nil }
return molecule
@ -35,7 +35,7 @@ import UIKit
open override func viewForMiddle() -> UIView? {
guard let middleModel = templateModel?.middle,
let molecule = ModelRegistry.createMolecule(middleModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: middleModel)
else { return nil }
return molecule
@ -43,7 +43,7 @@ import UIKit
override open func viewForBottom() -> UIView? {
guard let footerModel = templateModel?.footer,
let molecule = ModelRegistry.createMolecule(footerModel, delegateObject: delegateObjectIVar)
let molecule = generateMoleculeView(from: footerModel)
else { return nil }
return molecule

View File

@ -13,7 +13,8 @@ public protocol TextInputDidDeleteProtocol: AnyObject {
}
@objcMembers open class TextField: UITextField {
@objcMembers open class TextField: UITextField, ViewMaskingProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -23,6 +24,8 @@ public protocol TextInputDidDeleteProtocol: AnyObject {
/// Set to true to hide the blinking textField cursor.
public var hideBlinkingCaret = false
public var shouldMaskWhileRecording: Bool = true
//--------------------------------------------------
// MARK: - Delegate
//--------------------------------------------------
@ -97,6 +100,8 @@ extension TextField: MoleculeViewProtocol {
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
shouldMaskWhileRecording = model.shouldMaskRecordedView ?? true
}
open func reset() {

View File

@ -9,7 +9,8 @@
import UIKit
@objcMembers open class View: UIView, MoleculeViewProtocol {
@objcMembers open class View: UIView, MoleculeViewProtocol, ViewMaskingProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -18,6 +19,10 @@ import UIKit
private var initialSetupPerformed = false
public var shouldMaskWhileRecording: Bool {
return model?.shouldMaskRecordedView ?? false
}
//--------------------------------------------------
// MARK: - Initialization
//--------------------------------------------------

View File

@ -246,6 +246,27 @@ import MVMCore
manager?.newDataReceived?(in: self)
}
public func generateMoleculeView(from model: MoleculeModelProtocol) -> MoleculeViewProtocol? {
executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in
behavior.willSetupMolecule(with: model, updating: nil)
}
guard let moleculeView = ModelRegistry.createMolecule(model, delegateObject: delegateObjectIVar) else { return nil }
executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in
behavior.didSetupMolecule(view: moleculeView, withModel: model)
}
return moleculeView
}
public func updateMoleculeView(_ view: MoleculeViewProtocol, from model: MoleculeModelProtocol) {
executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in
behavior.willSetupMolecule(with: model, updating: view)
}
view.set(with: model, delegateObjectIVar, nil)
executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in
behavior.didSetupMolecule(view: view, withModel: model)
}
}
//--------------------------------------------------
// MARK: - Navigation Item
//--------------------------------------------------
@ -322,6 +343,14 @@ import MVMCore
}
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
executeBehaviors { [weak self] (behavior: PageVisibilityBehavior) in
behavior.willShowPage(self?.delegateObjectIVar)
}
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
@ -330,6 +359,14 @@ import MVMCore
}
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
executeBehaviors { [weak self] (behavior: PageVisibilityBehavior) in
behavior.willHidePage(self?.delegateObjectIVar)
}
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)

View File

@ -14,12 +14,17 @@ public protocol PageBehaviorHandlerProtocol {
public extension PageBehaviorHandlerProtocol {
/// Creates the behaviors and sets the variable.
mutating func createBehaviors(for model: PageBehaviorHandlerModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
guard let behaviorModels = model.behaviors else {
behaviors = behaviors?.filter { $0.transcendsPageUpdates }
if behaviors?.isEmpty ?? false {
behaviors = nil
}
guard let behaviorModels = model.behaviors else {
return
}
var behaviors: [PageBehaviorProtocol] = []
var behaviors: [PageBehaviorProtocol] = behaviors ?? []
for behaviorModel in behaviorModels {
do {

View File

@ -9,25 +9,50 @@
public protocol PageBehaviorProtocol: ModelHandlerProtocol {
/// Should the behavior persist regardless of page behavior model updates.
var transcendsPageUpdates: Bool { get }
/// Initializes the behavior with the model
init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?)
}
public extension PageBehaviorProtocol {
var transcendsPageUpdates: Bool { return false }
}
/**
Behavior conforming protocols. Behaviors will conform to one or more of these protocols to receive page lifecycle events that pertain to them.
*/
public protocol PageMoleculeTransformationBehavior: PageBehaviorProtocol {
func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?)
func willSetupMolecule(with model: MoleculeModelProtocol, updating view: MoleculeViewProtocol?)
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol)
}
public extension PageMoleculeTransformationBehavior {
// All optional.
func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {}
func willSetupMolecule(with model: MoleculeModelProtocol, updating view: MoleculeViewProtocol?) {}
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol) {}
}
public protocol PageVisibilityBehavior: PageBehaviorProtocol {
func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?)
func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?)
func willHidePage(_ delegateObject: MVMCoreUIDelegateObject?)
func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?)
}
public extension PageVisibilityBehavior {
// All optional.
func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?) {}
func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {}
func willHidePage(_ delegateObject: MVMCoreUIDelegateObject?) {}
func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?) {}
}
public protocol PageScrolledBehavior: PageBehaviorProtocol {
func pageScrolled(scrollView: UIScrollView, _ delegateObject: MVMCoreUIDelegateObject?)

View File

@ -13,6 +13,7 @@
@class MFViewController;
@class MFLoadingViewController;
@class NavigationController;
NS_ASSUME_NONNULL_BEGIN
@interface MVMCoreUISession : MVMCoreSessionObject
@ -25,18 +26,21 @@ NS_ASSUME_NONNULL_BEGIN
/// Tracks the current page type the user is currently viewing. KVO compliant.
@property (nonatomic, strong, nullable) NSString *currentPageType;
// for handscroll Animation on subclasses of MFScrollingViewController
/// for handscroll Animation on subclasses of MFScrollingViewController
@property (assign, nonatomic) BOOL enableHandScrollAnimation;
//indicates if the app launched successfully
/// indicates if the app launched successfully
@property (assign, nonatomic) BOOL launchAppLoadedSuccessfully;
// Allows a global overload of the title view of navigation item.
/// Allows a global overload of the title view of navigation item.
- (nullable UIView *)titleViewForController:(nonnull MFViewController *)controller;
// Sets up the session as delegate for standard load view controller. Pass the view controller that will be used to present and will be disabled when load view is presented.
/// Sets up the session as delegate for standard load view controller. Pass the view controller that will be used to present and will be disabled when load view is presented.
- (void)setupAsStandardLoadViewDelegate:(nonnull UIViewController *)mainViewController;
/// Applies additional behaviors to a controller according to the session. Allows for packages to add additional cross cutting concerns.
- (void)applyGlobalBehaviorsToController:(nonnull UIViewController *)viewController;
@end
NS_ASSUME_NONNULL_END

View File

@ -62,4 +62,8 @@
self.mainViewController.view.userInteractionEnabled = YES;
}
- (void)applyGlobalBehaviorsToController:(nonnull UIViewController *)viewController {
// Allow extending frameworks to apply behaviors to add cross cutting concerns to the base controllers.
}
@end