diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..d475e062 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,90 @@ +stages: +# - test + - download + - build + - deploy + +#test: +# stage: test +# script: +# - echo "This job tests something" +# tags: +# - xcode_12_2 + +download_artifacts: + stage: download + script: + - ./Scripts/download_dependencies.sh + only: + - branches + - develop + tags: + - bash_shell + environment: + name: oneartifactory + url: https://oneartifactoryprod.verizon.com/artifactory + variables: + ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory + +build_project: + stage: build + script: + - ./Scripts/build_aggregate.sh + only: + - branches + - develop + tags: + - xcode_12_2 + +deploy_snapshot: + stage: deploy + script: + - cd Scripts && ./upload_core_ui_frameworks.sh + only: + - branches + - develop + tags: + - bash_shell + environment: + name: oneartifactory + url: https://oneartifactoryprod.verizon.com/artifactory + variables: + ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory + +#promote_snapshot: +# stage: go live +# # Prevent artifacts from needing to re-download. Everything we need is in Artifactory. +# dependencies: [] +# script: +# # Grab the framework version from the xcode project. +# - framework_ver=$(cd RemoteView && agvtool vers -terse) +# - cd Scripts && ./promote_remote_view_frameworks.sh $framework_ver +# only: +# - tags +# tags: +# - bash_shell +# environment: +# name: oneartifactory +# url: https://oneartifactoryprod.verizon.com/artifactory +# variables: +# ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory +# +#create_version_tag: +# stage: tag +# when: manual +# # Prevent artifacts from needing to re-download. +# dependencies: [] +# script: +# # Grab the framework version from the xcode project and create a tag of the version. +# - framework_ver=$(cd RemoteView && agvtool vers -terse) +# - git tag -a "v${framework_ver}" -m "Version ${framework_ver} created by gitlab-ci Build" +# # Extract the git repo url to ssh version (git@gitlab.verizon.com) +# - ci_push_repo="git@${CI_SERVER_HOST}:${CI_PROJECT_PATH}.git" +# - echo $ci_push_repo +# # Set the remote url for pushing assuming the gitlab runner has SSH access to the repo. +# - git remote set-url --push origin $ci_push_repo +# - git push origin "v${framework_ver}" +# only: +# - develop +# tags: +# - bash_shell diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 85e99058..267767b4 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -121,6 +121,8 @@ 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */; }; 0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB423FF18D2004C5109 /* Arrow.swift */; }; 0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */; }; + 1D6D258826899B0C00DEBB08 /* ImageButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D6D258626899B0B00DEBB08 /* ImageButtonModel.swift */; }; + 1D6D258926899B0C00DEBB08 /* ImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D6D258726899B0B00DEBB08 /* ImageButton.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 */; }; @@ -323,7 +325,6 @@ D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; }; D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; }; D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B43252E00E4004B3F56 /* PageProtocol.swift */; }; - D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2169300251E51E7002A6324 /* SectionListTemplate.swift */; }; @@ -687,6 +688,8 @@ 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLinkModel.swift; sourceTree = ""; }; 0AE98BB423FF18D2004C5109 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = ""; }; 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowModel.swift; sourceTree = ""; }; + 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; }; 279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionModelAdapter.swift; sourceTree = ""; }; 27F6B08726051831008529AA /* MoleculeTreeTraversalProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTreeTraversalProtocol.swift; sourceTree = ""; }; 27F6B08B26052AFF008529AA /* ParentMoleculeModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentMoleculeModelProtocol.swift; sourceTree = ""; }; @@ -889,7 +892,6 @@ D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = ""; }; D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = ""; }; D20F3B43252E00E4004B3F56 /* PageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageProtocol.swift; sourceTree = ""; }; - D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarRefreshProtocol.swift; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D2169300251E51E7002A6324 /* SectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplate.swift; sourceTree = ""; }; @@ -1295,6 +1297,8 @@ 0AE98BAD23FEF92B004C5109 /* Link */ = { isa = PBXGroup; children = ( + 1D6D258726899B0B00DEBB08 /* ImageButton.swift */, + 1D6D258626899B0B00DEBB08 /* ImageButtonModel.swift */, D28A838823CCCFCB00DFE4FC /* LinkModel.swift */, C07065C32395677300FBF997 /* Link.swift */, 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */, @@ -2324,7 +2328,6 @@ D20F3B43252E00E4004B3F56 /* PageProtocol.swift */, 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */, - D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */, D2B9D0E3265EEE9D0084735C /* MoleculeListProtocol.swift */, 011B58EE23A2AA850085F53C /* ModelProtocols */, ); @@ -2760,7 +2763,6 @@ D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */, - D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */, D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */, D260105D23D0BCD400764D80 /* Stack.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, @@ -2894,6 +2896,7 @@ D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */, AA617AB02453010A00910B8F /* ListDeviceComplexLinkSmall.swift in Sources */, D23A8FD9260CE004007E14CE /* MFStyler+PaddingExtension.swift in Sources */, + 1D6D258926899B0C00DEBB08 /* ImageButton.swift in Sources */, C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */, 01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, @@ -2939,6 +2942,7 @@ BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */, 943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */, 011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */, + 1D6D258826899B0C00DEBB08 /* ImageButtonModel.swift in Sources */, AA07EA912510A442009A2AE3 /* StarModel.swift in Sources */, D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */, 011D95892404249B000E3791 /* FormHolderModelProtocol.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ImageButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ImageButton.swift new file mode 100644 index 00000000..2d71ee6e --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ImageButton.swift @@ -0,0 +1,54 @@ +// +// ImageButton.swift +// MobileFirstFramework +// +// Created by Chintakrinda, Arun Kumar (Arun) on 07/10/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ImageButton: Button { + + public let image = LoadImageView(pinnedEdges: .all) + + open override func setupView() { + super.setupView() + insertSubview(image, at: 0) + image.isUserInteractionEnabled = false + NSLayoutConstraint.constraintPinSubview(toSuperview: image) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let castModel = model as? ImageButtonModel else { + super.set(with: model, delegateObject, additionalData) + return + } + image.setOptional(with: castModel.image, delegateObject, additionalData) + castModel.updateUI = { [weak self] in + MVMCoreDispatchUtility.performBlock(onMainThread: { + self?.setState() + }) + } + + super.set(with: model, delegateObject, additionalData) + FormValidator.setupValidation(for: castModel, delegate: delegateObject?.formHolderDelegate) + + } + + private func setState() { + guard let castModel = model as? ImageButtonModel else { + return + } + if castModel.enabled { + if let enabledTintColor = castModel.enabledTintColor { + image.imageView.tintColor = enabledTintColor.uiColor + } + } else if let disabledTintColor = castModel.disabledTintColor { + image.imageView.tintColor = disabledTintColor.uiColor + } + } + + + +} diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ImageButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ImageButtonModel.swift new file mode 100644 index 00000000..d8217d0d --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ImageButtonModel.swift @@ -0,0 +1,95 @@ +// +// ImageButtonModel.swift +// MobileFirstFramework +// +// Created by Chintakrinda, Arun Kumar (Arun) on 07/10/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class ImageButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol, EnableableModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "imageButton" + public var backgroundColor: Color? + + public var image: ImageViewModel? + + public var accessibilityText: String? + public var action: ActionModelProtocol + public var enabled: Bool = true + public var enabledTintColor: Color? + public var disabledTintColor: Color? + + public var groupName: String = "" + + public func setValidity(_ valid: Bool, group: FormGroupRule) { + enabled = valid + updateUI?() + } + + public var updateUI: ActionBlock? + + public init(image: ImageViewModel?, action: ActionModelProtocol) { + self.image = image + self.action = action + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case image + case backgroundColor + case accessibilityText + case action + case enabled + case groupName + case enabledTintColor + case disabledTintColor + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + image = try typeContainer.decodeIfPresent(ImageViewModel.self, forKey: .image) + accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) + action = try typeContainer.decodeModel(codingKey: .action) + + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { + self.enabled = enabled + } + + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + self.groupName = groupName + } + + if let enabledTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTintColor) { + self.enabledTintColor = enabledTintColor + } + + if let disabledTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTintColor) { + self.disabledTintColor = disabledTintColor + } + + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(image, forKey: .image) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) + try container.encode(enabled, forKey: .enabled) + try container.encodeModel(action, forKey: .action) + try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encodeIfPresent(enabledTintColor, forKey: .enabledTintColor) + try container.encodeIfPresent(disabledTintColor, forKey: .disabledTintColor) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tag.swift b/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tag.swift index bc905a79..c6f7144f 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tag.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tag.swift @@ -24,7 +24,6 @@ import Foundation super.setupView() layer.borderColor = UIColor.mvmCoolGray6.cgColor layer.borderWidth = 1 - label.numberOfLines = 1 addSubview(label) NSLayoutConstraint.constraintPinSubview(label, pinTop: true, topConstant: 13, pinBottom: true, bottomConstant: 13, pinLeft: true, leftConstant: 15, pinRight: true, rightConstant: 15) } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/Tags/TagModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/Tags/TagModel.swift index e0247e71..56a86742 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/Tags/TagModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/Tags/TagModel.swift @@ -21,6 +21,12 @@ import Foundation case backgroundColor } + public func setDefaults() { + if label.numberOfLines == nil { + label.numberOfLines = 1 + } + } + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) label = try typeContainer.decode(LabelModel.self, forKey: .label) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index f1fd5fed..a5657dd4 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -124,7 +124,7 @@ import MVMCore didSet { if !updateSelectionOnly { layoutIfNeeded() - (model as? CheckboxModel)?.checked = isSelected + (model as? CheckboxModel)?.selected = isSelected shapeLayer?.removeAllAnimations() updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) @@ -419,8 +419,8 @@ import MVMCore isAnimated = model.animated isRound = model.round - if model.checked { - checkAndBypassAnimations(selected: model.checked) + if model.selected { + checkAndBypassAnimations(selected: model.selected) } isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index c2d9f14e..c4f69f27 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -6,10 +6,14 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import Foundation +/// Protocol to apply to any model of a UI Control with a binary on/off nature. +/// +/// Example classes: Checkbox or Toggle. +@objc public protocol SelectableMoleculeModelProtocol { + var selected: Bool { get set } +} - -@objcMembers public class CheckboxModel: MoleculeModelProtocol, FormFieldProtocol { +@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModelProtocol, FormFieldProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -17,7 +21,7 @@ import Foundation public static var identifier: String = "checkbox" public var backgroundColor: Color? public var accessibilityIdentifier: String? - public var checked: Bool = false + public var selected: Bool = false public var enabled: Bool = true public var animated: Bool = true public var inverted: Bool = false @@ -66,19 +70,19 @@ import Foundation case groupName case offAction } - + //-------------------------------------------------- - // MARK: - Methods + // MARK: - Form Validation //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { checked } + public func formFieldValue() -> AnyHashable? { selected } //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- public init(isChecked: Bool = false) { - self.checked = isChecked + self.selected = isChecked baseValue = isChecked } @@ -132,10 +136,10 @@ import Foundation } if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) { - self.checked = checked + self.selected = checked } - baseValue = checked + baseValue = selected if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) { self.animated = animated @@ -169,7 +173,7 @@ import Foundation try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(borderColor, forKey: .borderColor) try container.encode(borderWidth, forKey: .borderWidth) - try container.encode(checked, forKey: .checked) + try container.encode(selected, forKey: .checked) try container.encode(inverted, forKey: .inverted) try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(checkColor, forKey: .checkColor) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift index e8f940a2..c0f001e7 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift @@ -8,6 +8,10 @@ import Foundation +public protocol RadioBoxSelectionDelegate: class { + func selected(radioBox: RadioBoxModel) +} + open class RadioBoxes: View { public var collectionView: CollectionView! @@ -19,14 +23,14 @@ open class RadioBoxes: View { private var radioBoxesModel: RadioBoxesModel? { return model as? RadioBoxesModel } - + public weak var radioDelegate: RadioBoxSelectionDelegate? private var delegateObject: MVMCoreUIDelegateObject? /// The models for the molecules. public var boxes: [RadioBoxModel]? - + private var size: CGFloat? - + open override func layoutSubviews() { super.layoutSubviews() // Accounts for any collection size changes @@ -37,7 +41,7 @@ open class RadioBoxes: View { open func updateAccessibilityValue(collectionView: UICollectionView, cell: RadioBoxCollectionViewCell, indexPath: IndexPath) { guard let format = MVMCoreUIUtility.hardcodedString(withKey: "index_string_of_total"), - let indexString = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: indexPath.row + 1)) else { return } + let indexString = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: indexPath.row + 1)) else { return } let total = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) cell.accessibilityValue = String(format: format, indexString, total) } @@ -60,7 +64,7 @@ open class RadioBoxes: View { guard let model = model as? RadioBoxesModel else { return } boxes = model.boxes FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) - + backgroundColor = model.backgroundColor?.uiColor registerCells() @@ -85,7 +89,7 @@ open class RadioBoxes: View { layout.minimumInteritemSpacing = itemSpacing return layout } - + /// Creates the collection view. open func createCollectionView() -> CollectionView { let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout()) @@ -98,7 +102,7 @@ open class RadioBoxes: View { open func registerCells() { collectionView.register(RadioBoxCollectionViewCell.self, forCellWithReuseIdentifier: "RadioBoxCollectionViewCell") } - + // MARK: - JSON Setters open func setHeight() { guard let boxes = boxes, boxes.count > 0 else { @@ -127,7 +131,7 @@ extension RadioBoxes: UICollectionViewDataSource { open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let molecule = boxes?[indexPath.row], - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioBoxCollectionViewCell", for: indexPath) as? RadioBoxCollectionViewCell else { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioBoxCollectionViewCell", for: indexPath) as? RadioBoxCollectionViewCell else { fatalError() } cell.reset() @@ -162,6 +166,8 @@ extension RadioBoxes: UICollectionViewDelegate { cell.radioBox.selectBox() _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) cell.updateAccessibility() + guard let radioBox = boxes?[indexPath.row] else { return } + radioDelegate?.selected(radioBox: radioBox) } open func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index 304b23c4..3513a03b 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -98,7 +98,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) self.constrainKnob() } - toggleModel?.state = isOn + toggleModel?.selected = isOn _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") setNeedsLayout() @@ -381,7 +381,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) containerTintColor.off = model.offTintColor.uiColor knobTintColor.on = model.onKnobTintColor.uiColor knobTintColor.off = model.offKnobTintColor.uiColor - isOn = model.state + isOn = model.selected changeStateNoAnimation(isOn) isAnimated = model.animated isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index 4990df73..5be014fb 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift @@ -15,7 +15,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo public static var identifier: String = "toggle" public var accessibilityIdentifier: String? public var backgroundColor: Color? - public var state: Bool = false + public var selected: Bool = false public var animated: Bool = true public var enabled: Bool = true public var action: ActionModelProtocol? @@ -53,17 +53,17 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo } //-------------------------------------------------- - // MARK: - Methods + // MARK: - Form Valdiation //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { state } + public func formFieldValue() -> AnyHashable? { selected } //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- public init(_ state: Bool) { - self.state = state + self.selected = state baseValue = state } @@ -75,7 +75,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo let typeContainer = try decoder.container(keyedBy: CodingKeys.self) if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { - self.state = state + self.selected = state } if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { @@ -109,7 +109,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) - baseValue = state + baseValue = selected fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName @@ -123,7 +123,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) try container.encode(moleculeName, forKey: .moleculeName) - try container.encode(state, forKey: .state) + try container.encode(selected, forKey: .state) try container.encode(animated, forKey: .animated) try container.encode(enabled, forKey: .enabled) try container.encode(onTintColor, forKey: .onTintColor) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 026e9acf..395ab923 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -315,6 +315,10 @@ public typealias ActionBlock = () -> () if let color = labelModel.textColor { textColor = color.uiColor } + + if let lines = labelModel.numberOfLines { + numberOfLines = lines + } 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]) @@ -790,6 +794,7 @@ extension Label { accessibilityCustomActions = [] clauses = [] accessibilityTraits = .staticText + numberOfLines = 0 } public func needsToBeConstrained() -> Bool { true } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift index fbbc9e03..2eec132f 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift @@ -25,6 +25,7 @@ public var html: String? public var hero: Int? public var makeWholeViewClickable: Bool? + public var numberOfLines: Int? //-------------------------------------------------- // MARK: - Keys @@ -44,12 +45,13 @@ case html case hero case makeWholeViewClickable + case numberOfLines } enum AttributeTypeKey: String, CodingKey { case type } - + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- @@ -76,9 +78,10 @@ html = try typeContainer.decodeIfPresent(String.self, forKey: .html) hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero) makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable) + numberOfLines = try typeContainer.decodeIfPresent(Int.self, forKey: .numberOfLines) } - public func encode(to encoder: Encoder) throws { + open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(moleculeName, forKey: .moleculeName) try container.encode(text, forKey: .text) @@ -93,5 +96,6 @@ try container.encodeIfPresent(html, forKey: .html) try container.encodeIfPresent(hero, forKey: .hero) try container.encodeIfPresent(makeWholeViewClickable, forKey: .makeWholeViewClickable) + try container.encodeIfPresent(numberOfLines, forKey: .numberOfLines) } } diff --git a/MVMCoreUI/Atomic/Extensions/UITableViewRowAnimation+Extension.swift b/MVMCoreUI/Atomic/Extensions/UITableViewRowAnimation+Extension.swift new file mode 100644 index 00000000..712ba742 --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/UITableViewRowAnimation+Extension.swift @@ -0,0 +1,9 @@ +// +// UITableViewRowAnimation.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/26/21. +// Copyright © 2021 Verizon Wireless. All rights reserved. +// + +import Foundation diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRows.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRows.swift index a6d69e24..1baf057c 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRows.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRows.swift @@ -48,11 +48,8 @@ import Foundation open override func setupView() { super.setupView() setDefaults() - body.numberOfLines = 1 body.lineBreakMode = .byTruncatingTail - body2.numberOfLines = 1 body2.lineBreakMode = .byTruncatingTail - body3.numberOfLines = 1 body3.lineBreakMode = .byTruncatingTail verticalLine1.widthConstraint?.isActive = true diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRowsModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRowsModel.swift index 01bd72e4..5ca1eae1 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRowsModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRowsModel.swift @@ -49,6 +49,15 @@ public class HeadersH2PricingTwoRowsModel: HeaderModel, MoleculeModelProtocol { subBody?.attributes = [LabelAttributeStrikeThroughModel(0, subBody?.text.count ?? 0)] subBody2?.attributes = [LabelAttributeStrikeThroughModel(0, subBody2?.text.count ?? 0)] subBody3?.attributes = [LabelAttributeStrikeThroughModel(0, subBody3?.text.count ?? 0)] + if body.numberOfLines == nil { + body.numberOfLines = 1 + } + if body2.numberOfLines == nil { + body2.numberOfLines = 1 + } + if body3.numberOfLines == nil { + body3.numberOfLines = 1 + } } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinks.swift index fcd18f68..0c09e9e3 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinks.swift @@ -50,7 +50,6 @@ leftImage.contentMode = .scaleAspectFit rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinksModel.swift index 3129776b..a4fed00e 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinksModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinksModel.swift @@ -31,6 +31,10 @@ public class ListLeftVariableIconWithRightCaretAllTextLinksModel: ListItemModel, image.width = 30 image.height = 30 } + + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } } //----------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift index b9e4a3e1..6174fc4b 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift @@ -55,7 +55,6 @@ leftImage.contentMode = .scaleAspectFit rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyTextModel.swift index 891f37f5..633989d5 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyTextModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyTextModel.swift @@ -7,7 +7,7 @@ // -public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, MoleculeModelProtocol { +public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, ParentMoleculeModelProtocol { //----------------------------------------------------- // MARK: - Properties //----------------------------------------------------- @@ -17,6 +17,10 @@ public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, Mol public var headlineBody: HeadlineBodyModel public var rightLabel: LabelModel + public var children: [MoleculeModelProtocol] { + [image, headlineBody, rightLabel] + } + //----------------------------------------------------- // MARK: - Methods //----------------------------------------------------- @@ -29,6 +33,9 @@ public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, Mol } headlineBody.style = .item headlineBody.headline?.hero = 0 + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } } //----------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift index 79530eb6..9b85dc4f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift @@ -71,7 +71,6 @@ rightBar.widthAnchor.constraint(equalToConstant: 20).isActive = true rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() horizontalStack.restack() diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift index 5309ebe1..52c175df 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift @@ -45,6 +45,10 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol { rightBar.backgroundColor = Color(uiColor: .gray) } + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } + leftHeadline.hero = 0 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocator.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocator.swift index b10b3069..1e271e87 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocator.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocator.swift @@ -39,7 +39,6 @@ rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() horizontalStack.restack() diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocatorModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocatorModel.swift index 10a0cd29..44c4ef90 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocatorModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Miscellaneous/ListStoreLocatorModel.swift @@ -38,6 +38,9 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol { override public func setDefaults() { super.setDefaults() leftHeadline.hero = 0 + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift index ec58684c..d0b2ff70 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift @@ -61,7 +61,6 @@ rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinksModel.swift index 7b1e065a..8d6ba0cb 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinksModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinksModel.swift @@ -17,6 +17,17 @@ public class ListRightVariablePriceChangeAllTextAndLinksModel: ListItemModel, Mo public var rightLabel: LabelModel public var arrow: ArrowModel + //----------------------------------------------------- + // MARK: - Methods + //----------------------------------------------------- + + override public func setDefaults() { + super.setDefaults() + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } + } + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift index b503b57a..a03b676d 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift @@ -46,7 +46,6 @@ rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyTextModel.swift index e3b325b6..c7efdb07 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyTextModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyTextModel.swift @@ -37,6 +37,9 @@ public class ListRightVariablePriceChangeBodyTextModel: ListItemModel, MoleculeM if let headline = headlineBody.headline { headline.hero = 0 } + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinks.swift index 3a342b6a..12f11b9e 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinks.swift @@ -38,7 +38,6 @@ super.setupView() rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinksModel.swift index d3725ed1..e2bc1691 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinksModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableRightCaretAlltextAndLinksModel.swift @@ -7,7 +7,7 @@ // -public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { +public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, ParentMoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -16,6 +16,21 @@ public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, Mol public var rightLabel: LabelModel public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + public var children: [MoleculeModelProtocol] { + [rightLabel, eyebrowHeadlineBodyLink] + } + + //----------------------------------------------------- + // MARK: - Methods + //----------------------------------------------------- + + override public func setDefaults() { + super.setDefaults() + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } + } + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift index 3db13a01..ddb202b6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift @@ -39,9 +39,6 @@ open override func setupView() { super.setupView() - leftLabel.numberOfLines = 1 - centerLabel.numberOfLines = 1 - rightLabel.numberOfLines = 1 addMolecule(stack) stack.restack() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChangesModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChangesModel.swift index 00bf689c..9b121926 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChangesModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChangesModel.swift @@ -17,6 +17,23 @@ public class ListThreeColumnBillChangesModel: ListItemModel, MoleculeModelProtoc public var centerLabel: LabelModel public var rightLabel: LabelModel + //----------------------------------------------------- + // MARK: - Methods + //----------------------------------------------------- + + override public func setDefaults() { + super.setDefaults() + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } + if centerLabel.numberOfLines == nil { + centerLabel.numberOfLines = 1 + } + if leftLabel.numberOfLines == nil { + leftLabel.numberOfLines = 1 + } + } + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift index c083e457..b211c918 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift @@ -74,9 +74,6 @@ view.addSubview(rightVerticalStack) NSLayoutConstraint.pinViews(leftView: leftVerticalStack, rightView: rightVerticalStack, alignTop: true) - leftHeadline.numberOfLines = 1 - rightLabel.numberOfLines = 1 - rightSubLabel.numberOfLines = 1 } //---------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift index 822b5074..f88677a8 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift @@ -34,6 +34,18 @@ public class ListTwoColumnPriceDescriptionModel: ListItemModel, MoleculeModelPro if rightSubLabel.attributes == nil { rightSubLabel.attributes = [LabelAttributeStrikeThroughModel(0, rightSubLabel.text.count)] } + + if rightLabel.numberOfLines == nil { + rightLabel.numberOfLines = 1 + } + + if rightSubLabel.numberOfLines == nil { + rightSubLabel.numberOfLines = 1 + } + + if leftHeadline.numberOfLines == nil { + leftHeadline.numberOfLines = 1 + } } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChart.swift index 18de25f1..065c502a 100644 --- a/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChart.swift +++ b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChart.swift @@ -64,6 +64,7 @@ open class DoughnutChart: View { open override func reset() { super.reset() titleLabel.reset() + titleLabel.numberOfLines = 1 subTitleLabel.reset() clearLayers() } diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift index f3ce24e5..804075ff 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift @@ -13,7 +13,8 @@ import UIKit func didSelectItem(_ indexPath: IndexPath, tabs: Tabs) } -@objcMembers open class Tabs: View, MVMCoreUIViewConstrainingProtocol { +@objcMembers open class Tabs: View, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol { + public var tabsModel: TabsModel? { get { return model as? TabsModel } diff --git a/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabelsModel.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabelsModel.swift index 76a56178..5c910298 100644 --- a/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabelsModel.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabelsModel.swift @@ -8,7 +8,7 @@ import UIKit -public class CornerLabelsModel: MoleculeModelProtocol { +public class CornerLabelsModel: ParentMoleculeModelProtocol { public static var identifier: String = "cornerLabels" public var backgroundColor: Color? public var topLeftLabel: LabelModel? @@ -16,7 +16,10 @@ public class CornerLabelsModel: MoleculeModelProtocol { public var bottomLeftLabel: LabelModel? public var bottomRightLabel: LabelModel? public var molecule: MoleculeModelProtocol? - + public var children: [MoleculeModelProtocol] { + [molecule, topLeftLabel, topRightLabel, bottomLeftLabel, bottomRightLabel].compactMap { $0 } + } + init(with molecule: MoleculeModelProtocol?) { self.molecule = molecule } @@ -35,20 +38,20 @@ public class CornerLabelsModel: MoleculeModelProtocol { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) molecule = try typeContainer.decodeModelIfPresent(codingKey: .molecule) - topLeftLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .topLeftLabel) - topRightLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .topRightLabel) - bottomLeftLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .bottomLeftLabel) - bottomRightLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .bottomRightLabel) + topLeftLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .topLeftLabel) + topRightLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .topRightLabel) + bottomLeftLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .bottomLeftLabel) + bottomRightLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .bottomRightLabel) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeModelIfPresent(molecule, forKey: .molecule) - try container.encodeIfPresent(topLeftLabel, forKey: .topLeftLabel) - try container.encodeIfPresent(topRightLabel, forKey: .topRightLabel) - try container.encodeIfPresent(bottomLeftLabel, forKey: .bottomLeftLabel) - try container.encodeIfPresent(bottomRightLabel, forKey: .bottomRightLabel) + try container.encodeModelIfPresent(topLeftLabel, forKey: .topLeftLabel) + try container.encodeModelIfPresent(topRightLabel, forKey: .topRightLabel) + try container.encodeModelIfPresent(bottomLeftLabel, forKey: .bottomLeftLabel) + try container.encodeModelIfPresent(bottomRightLabel, forKey: .bottomRightLabel) try container.encode(moleculeName, forKey: .moleculeName) } } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift index 33ed39b8..50676cbc 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/ImageBarButtonItem.swift @@ -20,16 +20,9 @@ } /// Creates the item with the passed in action. - public static func create(with image: UIImage?, actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { + public static func create(with image: UIImage?, model: MoleculeModelProtocol & NavigationButtonModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { let button = create(with: image) - button.set(with: actionModel, delegateObject: delegateObject, additionalData: additionalData) - return button - } - - /// Creates the item with the passed in action map. - public static func create(with image: UIImage?, actionMap: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { - let button = create(with: image) - button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData) + button.set(with: model, delegateObject: delegateObject, additionalData: additionalData) return button } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift index 5175defe..1eb4c8a1 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/LabelBarButtonItem.swift @@ -21,16 +21,9 @@ } /// Creates the item with the passed in action. - public static func create(with title: String?, actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { + public static func create(with title: String?, model: NavigationLabelButtonModel, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { let button = create(with: title) - button.set(with: actionModel, delegateObject: delegateObject, additionalData: additionalData) - return button - } - - /// Creates the item with the passed in action map. - public static func create(with title: String?, actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { - let button = create(with: title) - button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData) + button.set(with: model, delegateObject: delegateObject, additionalData: additionalData) return button } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift index f18c9fa1..8ba75d1a 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift @@ -67,7 +67,7 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule /// Convenience function that creates a BarButtonItem for the model. public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image) - let buttonItem = ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) + let buttonItem = ImageBarButtonItem.create(with: uiImage, model: self, delegateObject: delegateObject, additionalData: additionalData) buttonItem.accessibilityIdentifier = accessibilityIdentifier ?? image if let accessibilityString = accessibilityText { buttonItem.accessibilityLabel = accessibilityString diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift index e52ea0d5..d72c6ae0 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationLabelButtonModel.swift @@ -7,16 +7,16 @@ // -public class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol { +open class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public var backgroundColor: Color? - public static var identifier: String = "navigationLabelButton" - public var accessibilityIdentifier: String? - public var title: String - public var action: ActionModelProtocol + open var backgroundColor: Color? + open class var identifier: String { "navigationLabelButton" } + open var accessibilityIdentifier: String? + open var title: String + open var action: ActionModelProtocol //-------------------------------------------------- // MARK: - Initializer @@ -62,7 +62,7 @@ public class NavigationLabelButtonModel: NavigationButtonModelProtocol, Molecule //-------------------------------------------------- /// Convenience function that creates a BarButtonItem for the model. - public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { - return LabelBarButtonItem.create(with: title, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) + open func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { + return LabelBarButtonItem.create(with: title, model: self, delegateObject: delegateObject, additionalData: additionalData) } } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index 5fab61f9..6a75718b 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -91,3 +91,16 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc try container.encodeModelIfPresent(titleView, forKey: .titleView) } } + +extension NavigationItemModel: ParentMoleculeModelProtocol { + public var children: [MoleculeModelProtocol] { + var children: [MoleculeModelProtocol] = [line, titleView, backButton].compactMap { $0 } + if let leftButtons = additionalLeftButtons { + children.append(contentsOf: leftButtons) + } + if let rightButtons = additionalRightButtons { + children.append(contentsOf: rightButtons) + } + return children + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift index bdcd3fe1..511f41a5 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift @@ -26,6 +26,9 @@ open class CollapsableNotificationModel: NotificationModel { open override func setDefaults() { super.setDefaults() + if topLabel.numberOfLines == nil { + topLabel.numberOfLines = 1 + } if topLabel.textColor == nil { topLabel.textColor = Color(uiColor: .white) } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift index c23edeae..b3223fc7 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationTopView.swift @@ -11,7 +11,6 @@ import Foundation @objcMembers open class CollapsableNotificationTopView: View { public let label: Label = { let label = Label(fontStyle: .BoldBodySmall) - label.numberOfLines = 1 label.textAlignment = .center label.setContentHuggingPriority(.defaultHigh, for: .vertical) return label diff --git a/MVMCoreUI/Atomic/Protocols/MoleculeDelegateProtocol.swift b/MVMCoreUI/Atomic/Protocols/MoleculeDelegateProtocol.swift index c0bdc031..25bddd08 100644 --- a/MVMCoreUI/Atomic/Protocols/MoleculeDelegateProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/MoleculeDelegateProtocol.swift @@ -6,13 +6,13 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -import Foundation public protocol MoleculeDelegateProtocol: AnyObject { - + func getRootMolecules() -> [MoleculeModelProtocol] /// returns a module for the corresponding module name. - func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? + func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? + func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? } diff --git a/MVMCoreUI/Atomic/Protocols/MoleculeTreeTraversalProtocol.swift b/MVMCoreUI/Atomic/Protocols/MoleculeTreeTraversalProtocol.swift index 4e7efabb..d10a2d19 100644 --- a/MVMCoreUI/Atomic/Protocols/MoleculeTreeTraversalProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/MoleculeTreeTraversalProtocol.swift @@ -28,7 +28,7 @@ public protocol MoleculeTreeTraversalProtocol { // Helper Extensions // -extension MoleculeTreeTraversalProtocol { +public extension MoleculeTreeTraversalProtocol { func countMolecules(options: TreeTraversalOptions = .parentFirst) -> Int { return reduceDepthFirstTraverse(options: options, depth: 0, initialResult: 0) { (accumulator, molecule, depth) in @@ -50,5 +50,4 @@ extension MoleculeTreeTraversalProtocol { return accumulator } } - } diff --git a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift deleted file mode 100644 index 2d4002e3..00000000 --- a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// NavigationBarRefreshProtocol.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 10/8/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -/// A protocol to inform that we should refresh the navigation bar ui. -@objc public protocol NavigationBarRefreshProtocol { - @objc func refreshNavigationUI() -} - -extension UIViewController: NavigationBarRefreshProtocol { - - /// Convenience function to refresh the navigation bar ui. A separate function for others to use. - @objc public static func refreshNavigationUI(for viewController: UIViewController) { - guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } - if let navigationController = viewController.navigationController { - NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) - MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model) - } - if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { - // Refresh the manager if possible. - manager.refreshNavigationUI() - } - } - - public func refreshNavigationUI() { - UIViewController.refreshNavigationUI(for: self) - } -} diff --git a/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift index 0d33d57f..f3bd5d83 100644 --- a/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift @@ -66,7 +66,7 @@ return molecule } - open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { + open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false } // This template requires atleast one of the three layers. diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 568edb8a..554cfda1 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -177,6 +177,98 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol tableView.beginUpdates() tableView.endUpdates() } + + //-------------------------------------------------- + // MARK: - MoleculeDelegateProtocol + //-------------------------------------------------- + + open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { + guard let tableView = tableView else { return } + if let indexPath = tableView.indexPathForRow(at: molecule.center), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { + performTableViewUpdates() + } + } + + open override func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { + guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in + //TODO: check for molecule protocol eqaulity + let json = moleculeInfo.molecule.toJSON() + return json == molecule.toJSON() + }) else { return nil } + + return IndexPath(row: index, section: 0) + } + + open override func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { + // This dispatch is needed to fix a race condition that can occur if this function is called during the table setup. + DispatchQueue.main.async { + var indexPaths: [IndexPath] = [] + + for molecule in molecules { + if let info = self.createMoleculeInfo(with: molecule) { + self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) + let index = indexPath.row + 1 + indexPaths.count + self.moleculesInfo?.insert(info, at: index) + indexPaths.append(IndexPath(row: index, section: 0)) + } + } + + guard indexPaths.count > 0 else { return } + self.tableView?.insertRows(at: indexPaths, with: animation) + self.updateViewConstraints() + self.view.layoutIfNeeded() + } + } + + open func newData(for molecule: MoleculeModelProtocol) { + //TODO: expand for header, navigation, etc + let json = molecule.toJSON() + guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in + //TODO: check for molecule protocol eqaulity + if json == moleculeInfo.molecule.toJSON() { + return true + } else if let parent = moleculeInfo.molecule as? ParentMoleculeModelProtocol { + // Get all molecules of the same type for faster check. + let molecules: [MoleculeModelProtocol] = parent.reduceDepthFirstTraverse(options: .childFirst, depth: 0, initialResult: []) { (accumulator, currentMolecule, depth) in + if currentMolecule.moleculeName == molecule.moleculeName { + return accumulator + [currentMolecule] + } + return accumulator + } + for molecule in molecules { + if json == molecule.toJSON() { + return true + } + } + } + return false + }) else { return } + + // Refresh the cell. (reload loses cell selection) + let selectedIndex = tableView.indexPathForSelectedRow + let indexPath = IndexPath(row: index, section: 0) + tableView.reloadRows(at: [indexPath], with: .automatic) + if let selectedIndex = selectedIndex { + tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none) + } + } + + open override func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { + var indexPaths: [IndexPath] = [] + //TODO: check for molecule protocol equality + + for molecule in molecules { + if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) { + moleculesInfo?.remove(at: removeIndex) + indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) + } + } + + guard indexPaths.count > 0 else { return } + tableView?.deleteRows(at: indexPaths, with: animation) + updateViewConstraints() + view.layoutIfNeeded() + } //-------------------------------------------------- // MARK: - Convenience @@ -245,57 +337,91 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } extension MoleculeListTemplate: MoleculeListProtocol { - public func removeMolecules(at indexPaths: [IndexPath], animation: UITableView.RowAnimation?) { - for (index, indexPath) in indexPaths.sorted().enumerated() { - let removeIndex = indexPath.row - index - moleculesInfo?.remove(at: removeIndex) - } - - guard let animation = animation, - indexPaths.count > 0 else { return } - tableView?.deleteRows(at: indexPaths, with: animation) - updateViewConstraints() - view.layoutIfNeeded() - } - - public func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation?) { - // This dispatch is needed to fix a race condition that can occur if this function is called during the table setup. - DispatchQueue.main.async { - var indexPaths: [IndexPath] = [] - - for molecule in molecules { - if let info = self.createMoleculeInfo(with: molecule) { - self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) - let index = indexPath.row + indexPaths.count - self.moleculesInfo?.insert(info, at: index) - indexPaths.append(IndexPath(row: index, section: 0)) - } - } - - guard let animation = animation, - indexPaths.count > 0 else { return } - self.tableView?.insertRows(at: indexPaths, with: animation) - self.updateViewConstraints() - self.view.layoutIfNeeded() - } - } - - open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { - guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in - //TODO: check for molecule protocol eqaulity - let json = moleculeInfo.molecule.toJSON() - return json == molecule.toJSON() - }) else { return nil } - - return IndexPath(row: index, section: 0) - } - - open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { - guard let tableView = tableView else { return } - - let point = molecule.convert(molecule.bounds.origin, to: tableView) - if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { - performTableViewUpdates() - } - } +open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { +guard let tableView = tableView else { return } +if let indexPath = tableView.indexPathForRow(at: molecule.center), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { +performTableViewUpdates() +} +} + +open override func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { +guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in +//TODO: check for molecule protocol eqaulity +let json = moleculeInfo.molecule.toJSON() +return json == molecule.toJSON() +}) else { return nil } + +return IndexPath(row: index, section: 0) +} + +open override func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { +// This dispatch is needed to fix a race condition that can occur if this function is called during the table setup. +DispatchQueue.main.async { +var indexPaths: [IndexPath] = [] + +for molecule in molecules { +if let info = self.createMoleculeInfo(with: molecule) { +self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) +let index = indexPath.row + 1 + indexPaths.count +self.moleculesInfo?.insert(info, at: index) +indexPaths.append(IndexPath(row: index, section: 0)) +} +} + +guard indexPaths.count > 0 else { return } +self.tableView?.insertRows(at: indexPaths, with: animation) +self.updateViewConstraints() +self.view.layoutIfNeeded() +} +} + +open func newData(for molecule: MoleculeModelProtocol) { +//TODO: expand for header, navigation, etc +let json = molecule.toJSON() +guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in +//TODO: check for molecule protocol eqaulity +if json == moleculeInfo.molecule.toJSON() { +return true +} else if let parent = moleculeInfo.molecule as? ParentMoleculeModelProtocol { +// Get all molecules of the same type for faster check. +let molecules: [MoleculeModelProtocol] = parent.reduceDepthFirstTraverse(options: .childFirst, depth: 0, initialResult: []) { (accumulator, currentMolecule, depth) in +if currentMolecule.moleculeName == molecule.moleculeName { +return accumulator + [currentMolecule] +} +return accumulator +} +for molecule in molecules { +if json == molecule.toJSON() { +return true +} +} +} +return false +}) else { return } + +// Refresh the cell. (reload loses cell selection) +let selectedIndex = tableView.indexPathForSelectedRow +let indexPath = IndexPath(row: index, section: 0) +tableView.reloadRows(at: [indexPath], with: .automatic) +if let selectedIndex = selectedIndex { +tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none) +} +} + +open override func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { +var indexPaths: [IndexPath] = [] +//TODO: check for molecule protocol equality + +for molecule in molecules { +if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) { +moleculesInfo?.remove(at: removeIndex) +indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) +} +} + +guard indexPaths.count > 0 else { return } +tableView?.deleteRows(at: indexPaths, with: animation) +updateViewConstraints() +view.layoutIfNeeded() +} } diff --git a/MVMCoreUI/Atomic/Templates/StackPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/StackPageTemplateModel.swift index 27769b20..1ed663c2 100644 --- a/MVMCoreUI/Atomic/Templates/StackPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/StackPageTemplateModel.swift @@ -16,7 +16,7 @@ public var moleculeStack: StackModel public override var rootMolecules: [MoleculeModelProtocol] { - return [header, moleculeStack, footer].compactMap { $0 } + [navigationBar, header, moleculeStack, footer].compactMap { $0 } } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Templates/TemplateModel.swift b/MVMCoreUI/Atomic/Templates/TemplateModel.swift index 4f565e85..45c2ed1a 100644 --- a/MVMCoreUI/Atomic/Templates/TemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/TemplateModel.swift @@ -13,8 +13,7 @@ import Foundation //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - - public class var identifier: String { "" } + open class var identifier: String { "" } public var pageType: String public var template: String { @@ -74,7 +73,7 @@ import Foundation tabBarIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .tabBarIndex) } - public func encode(to encoder: Encoder) throws { + open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(pageType, forKey: .pageType) try container.encode(template, forKey: .template) diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift index 6a1bb244..06d813ef 100644 --- a/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift @@ -18,7 +18,7 @@ public var footer: MoleculeModelProtocol? public override var rootMolecules: [MoleculeModelProtocol] { - return [header, footer].compactMap { $0 } + [navigationBar, header, footer].compactMap { $0 } } //-------------------------------------------------- diff --git a/MVMCoreUI/BaseClasses/BarButtonItem.swift b/MVMCoreUI/BaseClasses/BarButtonItem.swift index d41fd57f..b46647f2 100644 --- a/MVMCoreUI/BaseClasses/BarButtonItem.swift +++ b/MVMCoreUI/BaseClasses/BarButtonItem.swift @@ -20,7 +20,7 @@ public typealias BarButtonAction = (BarButtonItem) -> () //-------------------------------------------------- // MARK: - Delegate //-------------------------------------------------- - + open var model: (MoleculeModelProtocol & NavigationButtonModelProtocol)? open weak var buttonDelegate: ButtonDelegateProtocol? var actionDelegate: ActionDelegate? @@ -28,18 +28,12 @@ public typealias BarButtonAction = (BarButtonItem) -> () // MARK: - Methods //-------------------------------------------------- - open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + open func set(with model: MoleculeModelProtocol & NavigationButtonModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + self.model = model buttonDelegate = delegateObject?.buttonDelegate actionDelegate?.buttonAction = { sender in - Button.performButtonAction(with: actionModel, button: sender, delegateObject: delegateObject, additionalData: additionalData) - } - } - - open func set(with actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - buttonDelegate = delegateObject?.buttonDelegate - actionDelegate?.buttonAction = { sender in - guard delegateObject?.buttonDelegate?.button?(sender, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true else { return } - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) + Button.performButtonAction(with: model.action, button: sender, delegateObject: delegateObject, additionalData: additionalDataWithSource) } } } diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 049b3463..59a06018 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -114,17 +114,6 @@ import UIKit try parsePageJSON() MVMCoreDispatchUtility.performBlock(onMainThread: { self.handleNewDataAndUpdateUI() - - if MVMCoreUIUtility.getCurrentVisibleController() == self { - // Update navigation bar if showing. - self.setNavigationBar() - self.manager?.refreshNavigationUI() - } - // Update splitview properties - if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { - MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(self.bottomProgress() ?? 0) - self.updateTabBar() - } }) } catch { if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") { @@ -133,7 +122,7 @@ import UIKit } } - open func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { + open func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { pageType = loadObject.pageType self.loadObject = loadObject @@ -197,7 +186,7 @@ import UIKit open func parsePageJSON() throws { } - open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer) -> Bool { + open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer) -> Bool { guard let pageType = loadObject?.pageType, var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), !modulesRequired.isEmpty @@ -237,6 +226,11 @@ import UIKit /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, open func handleNewData() { + if model?.navigationBar == nil { + let navigationItem = createDefaultLegacyNavigationModel() + model?.navigationBar = navigationItem + } + executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in behavior.onPageNew(rootMolecules: getRootMolecules(), delegateObjectIVar) } @@ -250,8 +244,14 @@ import UIKit view.backgroundColor = backgroundColor.uiColor } - // Sets up the navigation item based on the data. - setNavigationItem() + // Update splitview properties + if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { + MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(bottomProgress() ?? 0) + updateTabBar() + } + + // Notify the manager of new data + manager?.newDataReceived?(in: self) } //-------------------------------------------------- @@ -267,28 +267,6 @@ import UIKit return model?.navigationBar } - /// Sets the navigation item for this view controller. - open func setNavigationItem() { - guard let navigationItemModel = getNavigationModel(), - let navigationController = navigationController - else { return } - - // Utilize helper function to set the navigation item state. - NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) - } - - /// Sets the appearance of the navigation bar based on the model. - open func setNavigationBar() { - guard let navigationItemModel = getNavigationModel(), - let navigationController = navigationController else { - MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() - return - } - - // Utilize helper function to set the split view and navigation item state. - MVMCoreUISplitViewController.setNavigationBarUI(for: self, navigationController: navigationController, navigationItemModel: navigationItemModel) - } - //-------------------------------------------------- // MARK: - TabBar //-------------------------------------------------- @@ -378,9 +356,6 @@ import UIKit updateTabBar() } - // Update the navigation bar ui when view is appearing. - setNavigationBar() - // Track. MVMCoreUISession.sharedGlobal()?.currentPageType = pageType MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) @@ -475,10 +450,12 @@ import UIKit addFormParams(requestParameters: requestParameters, actionInformation: actionInformation, additionalData: additionalData) requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") var pageForwardedData = additionalData ?? [:] + executeBehaviors { (behavior: PageLocalDataShareBehavior) in let dataMap = behavior.compileLocalPageDataForTransfer(delegateObjectIVar) - pageForwardedData.merge(dataMap) { (current, _) in current } + pageForwardedData.merge(dataMap) { current, _ in current } } + MVMCoreActionHandler.defaultHandleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: pageForwardedData, delegateObject: delegateObject()) } @@ -486,14 +463,16 @@ import UIKit MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: self, actionInformation: actionInformation, additionalData: additionalData) } - open func handleUnknownActionType(_ actionType: String?, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) { + open func handleUnknownActionType(_ actionType: String?, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { var handled = false + executeBehaviors { (behavior: PageCustomActionHandlerBehavior) in - if (!handled) { + if !handled { handled = behavior.handleAction(type: actionType, information: actionInformation, additionalData: additionalData) } } - if (!handled) { + + if !handled { MVMCoreUIActionHandler.defaultHandleUnknownActionType(actionType, actionInformation: actionInformation, additionalData: additionalData, delegateObject: delegateObjectIVar) } } @@ -503,7 +482,7 @@ import UIKit //-------------------------------------------------- open func getRootMolecules() -> [MoleculeModelProtocol] { - return model?.rootMolecules ?? [] + model?.rootMolecules ?? [] } open func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? { @@ -530,11 +509,6 @@ import UIKit // MARK: - MVMCoreUIDetailViewProtocol //-------------------------------------------------- - // Reset the navigation state. - public func splitViewDidReset() { - setNavigationBar() - } - public func isLeftPanelAccessible() -> Bool { // TODO: Remove when hamburger menu is fully phased out. if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { diff --git a/MVMCoreUI/Behaviors/GetContactBehavior.swift b/MVMCoreUI/Behaviors/GetContactBehavior.swift index 1bb27ac7..77c0d7f4 100644 --- a/MVMCoreUI/Behaviors/GetContactBehavior.swift +++ b/MVMCoreUI/Behaviors/GetContactBehavior.swift @@ -9,6 +9,7 @@ import Foundation import Contacts + public protocol PageGetContactBehaviorConsumerProtocol { func getMatchParameters() -> (NSPredicate, [CNKeyDescriptor])? func consume(contacts: [CNContact]) @@ -18,7 +19,7 @@ public class PageGetContactBehaviorModel: PageBehaviorModelProtocol { public class var identifier: String { "pageGetContactBehavior" } public var shouldAllowMultipleInstances: Bool { false } - public init() {} + public init() { } } public class PageGetContactBehavior: PageVisibilityBehavior { @@ -41,7 +42,8 @@ public class PageGetContactBehavior: PageVisibilityBehavior { // Tell template to update MVMCoreDispatchUtility.performBlock(onMainThread: { // TODO: move to protocol function instead - (self?.delegate?.moleculeDelegate as? ViewController)?.handleNewDataAndUpdateUI() + guard let controller = self?.delegate?.moleculeDelegate as? ViewController else { return } + controller.handleNewDataAndUpdateUI() }) } } diff --git a/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift b/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift index e7db54bc..3f14f3cc 100644 --- a/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift +++ b/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift @@ -41,6 +41,11 @@ public protocol PageLocalDataShareBehavior: PageBehaviorProtocol { public protocol PageCustomActionHandlerBehavior: PageBehaviorProtocol { + /// - Parameters: + /// - actionType: The action type of the passed action model. + /// - information: information of the passed action model. + /// - additionalData: Additional information of the + /// - Returns: Boolean determines if the action has been handled. func handleAction(type actionType: String?, information: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> Bool } diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 331656b7..eb378b08 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -8,8 +8,9 @@ import UIKit -@objcMembers open class NavigationController: UINavigationController { +@objcMembers open class NavigationController: UINavigationController, MVMCoreViewManagerViewControllerProtocol { public var separatorView: Line? + public var manager: (UIViewController & MVMCoreViewManagerProtocol)? /// Getter for the main navigation controller public static func navigationController() -> Self? { @@ -33,6 +34,7 @@ import UIKit MVMCoreUISession.sharedGlobal()?.navigationController = navigationController MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController MVMCoreNavigationHandler.shared()?.navigationController = navigationController + MVMCoreNavigationHandler.shared()?.addDelegate(navigationController) return navigationController } @@ -103,4 +105,83 @@ import UIKit viewController.navigationItem.titleView = molecule } } + + /// Convenience function to return the navigation model of the lowest controller traversing managers if applicable. + public func getNavigationModel(from viewController: UIViewController) -> NavigationItemModelProtocol? { + guard let topViewController = topViewController, + viewController == MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController), + let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return nil } + return model + } +} + +extension NavigationController: MVMCoreViewManagerProtocol { + public func getCurrentViewController() -> UIViewController? { + guard let topViewController = topViewController else { return nil } + return MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController) + } + + public func containsPage(withPageType pageType: String?) -> Bool { + for controller in viewControllers { + if let manager = controller as? MVMCoreViewManagerProtocol, + manager.containsPage(withPageType: pageType) { + return true + } else if let controller = controller as? MVMCoreViewControllerProtocol, + controller.pageType == pageType { + return true + } + } + return false + } + + public func newDataReceived(in viewController: UIViewController) { + if let topViewController = topViewController, + let model = getNavigationModel(from: viewController) { + Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: topViewController) + Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: topViewController) + } + manager?.newDataReceived?(in: viewController) + } + + public func willDisplay(_ viewController: UIViewController) { + if let topViewController = topViewController, + let model = getNavigationModel(from: viewController) { + Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: topViewController) + } + manager?.willDisplay?(viewController) + } + + public func displayedViewController(_ viewController: UIViewController) { + if let topViewController = topViewController, + let model = getNavigationModel(from: viewController) { + Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: topViewController) + } + manager?.displayedViewController?(viewController) + } +} + +extension NavigationController: MVMCorePresentationDelegateProtocol { + public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) { + guard self == navigationController else { return } + if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) { + MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller) + } + guard let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return } + if let model = getNavigationModel(from: newViewController) { + Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: viewController) + } + manager?.willDisplay?(newViewController) + } + + public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) { + guard self == navigationController, + let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return } + if let model = getNavigationModel(from: newViewController) { + Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: viewController) + } + manager?.displayedViewController?(newViewController) + if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) { + controller.viewControllerReady?(inManager: self) + } + } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h index 60ce0873..bd81fec5 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h @@ -48,9 +48,6 @@ NS_ASSUME_NONNULL_BEGIN - (UIStatusBarStyle)defaultStatusBarStyle; - (nullable UIColor *)defaultStatusBarBackgroundColor; -/// Called when the split view did reset. If this function found, the splitview assumes it is handling the split view state and does not do anything. If you have navigation item buttons, override this function and handle the panels manually. -- (void)splitViewDidReset; - @end NS_ASSUME_NONNULL_END diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index eb22eccb..bb1b24d5 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -8,15 +8,15 @@ import Foundation +// Navigation bar update functions public extension MVMCoreUISplitViewController { /// Convenience function. Sets the navigation and split view properties for the view controller. Panel access is determined if view controller is a detail view protocol. static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { guard let splitView = MVMCoreUISplitViewController.main(), navigationController == splitView.navigationController, - navigationController.topViewController == viewController else { + viewController == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() else { /// Not the split view navigation controller, skip split functions. - NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) return } splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel) @@ -24,8 +24,6 @@ public extension MVMCoreUISplitViewController { /// Sets the navigation item for the view controller based on the model and splitview controller private func set(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { - NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - setLeftPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) setRightPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) @@ -37,6 +35,8 @@ public extension MVMCoreUISplitViewController { /// Sets the left navigation items for the view controller based on model and splitview. func setLeftNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { + guard let topViewController = navigationController.topViewController else { return } + var leftItems: [UIBarButtonItem] = [] let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject @@ -75,11 +75,13 @@ public extension MVMCoreUISplitViewController { leftItems.append(contentsOf: additionalLeftButtons) } - viewController.navigationItem.setLeftBarButtonItems(leftItems.count > 0 ? leftItems : nil, animated: !DisableAnimations.boolValue) + topViewController.navigationItem.setLeftBarButtonItems(leftItems.count > 0 ? leftItems : nil, animated: !DisableAnimations.boolValue) } /// Sets the right navigation items for the view controller based on model and splitview. func setRightNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { + guard let topViewController = navigationController.topViewController else { return } + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject var rightItems: [UIBarButtonItem] = [] @@ -102,6 +104,38 @@ public extension MVMCoreUISplitViewController { rightItems.append(contentsOf: additionalRightButtons) } - viewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue) + topViewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue) + } + + @objc func navigationBarModelExists() -> Bool { + // Legacy Navigation + guard let currentViewController = getCurrentDetailViewController(), + let _ = navigationController?.getNavigationModel(from: currentViewController) else { return false } + return true + } + + /// Convenience function to update the navigation bar if the controller is the current lowest controller. + @objc func updateNavigationBarFor(viewController: UIViewController) { + guard let navigationController = navigationController, + let model = navigationController.getNavigationModel(from: viewController) else { return } + set(for: viewController, navigationController: navigationController, navigationItemModel: model) + } +} + +extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol { + public func getCurrentViewController() -> UIViewController? { + navigationController?.getCurrentViewController() + } + + public func containsPage(withPageType pageType: String?) -> Bool { + navigationController?.containsPage(withPageType: pageType) ?? false + } + + public func displayedViewController(_ viewController: UIViewController) { + updateNavigationBarFor(viewController: viewController) + } + + public func newDataReceived(in viewController: UIViewController) { + updateNavigationBarFor(viewController: viewController) } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h index c48c9e8c..9c07374d 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h @@ -126,6 +126,9 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) { - (UIStatusBarStyle)getDefaultStatusBarStyle; - (nullable UIColor *)getDefaultStatusBarBackgroundColor; +/// Returns true if a panel is showing. +- (BOOL)isAPanelShowing; + #pragma mark - Main Subclassables // Can subclass to set threshold for when the drawers are permanently extended. Default is 1000 for the left panel and 2000 for both. diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 7377701b..d8639b56 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -240,7 +240,7 @@ CGFloat const PanelAnimationDuration = 0.2; - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *leftBarButtonItems = [NSMutableArray array]; - if (viewController.navigationController && [MVMCoreNavigationHandler.sharedNavigationHandler getViewControllersForNavigationController:viewController.navigationController].count > 1) { + if (self.navigationController && [MVMCoreNavigationHandler.sharedNavigationHandler getViewControllersForNavigationController:self.navigationController].count > 1) { [leftBarButtonItems addObject:self.backButton]; } if ((accessible && !extended) && self.leftPanelButton) { @@ -250,7 +250,7 @@ CGFloat const PanelAnimationDuration = 0.2; if (extraButtons) { [leftBarButtonItems addObjectsFromArray:extraButtons]; } - [viewController.navigationItem setLeftBarButtonItems:(leftBarButtonItems.count > 0 ? leftBarButtonItems : nil) animated:!DisableAnimations]; + [self.navigationController.topViewController.navigationItem setLeftBarButtonItems:(leftBarButtonItems.count > 0 ? leftBarButtonItems : nil) animated:!DisableAnimations]; } - (void)setLeftPanelIsAccessible:(BOOL)leftPanelIsAccessible forViewController:(UIViewController *)viewController updateNavigationButtons:(BOOL)updateNavigationButtons { @@ -422,7 +422,7 @@ CGFloat const PanelAnimationDuration = 0.2; if (extraButtons) { [navigationItems addObjectsFromArray:extraButtons]; } - [viewController.navigationItem setRightBarButtonItems:(navigationItems.count > 0 ? navigationItems : nil) animated:!DisableAnimations]; + [self.navigationController.topViewController.navigationItem setRightBarButtonItems:(navigationItems.count > 0 ? navigationItems : nil) animated:!DisableAnimations]; } - (BOOL)shouldExtendRightPanel { @@ -857,6 +857,7 @@ CGFloat const PanelAnimationDuration = 0.2; // The main view. NavigationController *navigationController = [NavigationController setupNavigationController]; + navigationController.manager = self; self.navigationController = navigationController; UIView *mainView = navigationController.view; @@ -988,9 +989,11 @@ CGFloat const PanelAnimationDuration = 0.2; } - (void)resetDrawers { - if ([self.navigationItemViewController respondsToSelector:@selector(splitViewDidReset)]) { - [((UIViewController *)self.navigationItemViewController) splitViewDidReset]; + if (!self.navigationItemViewController) { return; } + if ([self navigationBarModelExists]) { + [self updateNavigationBarForViewController:self.navigationItemViewController]; } else { + // Legacy [self setLeftPanelIsAccessible:self.leftPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES]; [self setRightPanelIsAccessible:self.rightPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES]; @@ -1029,6 +1032,10 @@ CGFloat const PanelAnimationDuration = 0.2; return nil; } +- (BOOL)isAPanelShowing { + return fabs(self.mainViewLeading.constant) > 1; +} + #pragma mark - Getters // Returns the desired view or falls back. Hot fix until we can get away from using these functions... @@ -1046,7 +1053,11 @@ CGFloat const PanelAnimationDuration = 0.2; } + (CGFloat)getApplicationViewWidth { - return CGRectGetWidth([self getBounds:[MVMCoreUISession sharedGlobal].splitViewController.view.superview]); + __block CGFloat width; + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ + width = CGRectGetWidth([self getBounds:[MVMCoreUISession sharedGlobal].splitViewController.view.superview]); + }]; + return width; } + (CGFloat)getApplicationViewMaxSize { @@ -1062,10 +1073,7 @@ CGFloat const PanelAnimationDuration = 0.2; } // if it is not presented viewcontroller, existing BAU logic will be working if (!viewController) { - viewController = self.navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController]; } return viewController; } @@ -1073,10 +1081,7 @@ CGFloat const PanelAnimationDuration = 0.2; - (UIViewController *)getCurrentDetailViewController { __block UIViewController *viewController = nil; [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ - viewController = self.navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController]; }]; return viewController; } diff --git a/MVMCoreUI/Containers/Views/Container.swift b/MVMCoreUI/Containers/Views/Container.swift index e3512bab..5b2e83eb 100644 --- a/MVMCoreUI/Containers/Views/Container.swift +++ b/MVMCoreUI/Containers/Views/Container.swift @@ -53,31 +53,28 @@ open class Container: View, ContainerProtocol { open func constrainView(_ view: UIView) { containerHelper.constrainView(view) } -} -// MARK: - MVMCoreViewProtocol -public extension Container { - - override func updateView(_ size: CGFloat) { + // MARK: - MVMCoreViewProtocol + open override func updateView(_ size: CGFloat) { super.updateView(size) (view as? MVMCoreViewProtocol)?.updateView(size) containerHelper.updateViewMargins(self, model: containerModel, size: size) } - + /// Will be called only once. - override func setupView() { + open override func setupView() { super.setupView() isAccessibilityElement = false backgroundColor = .clear } - - func addAndContain(_ view: UIView) { + + open func addAndContain(_ view: UIView) { view.translatesAutoresizingMaskIntoConstraints = false addSubview(view) containerHelper.constrainView(view) self.view = view } - + convenience init(andContain view: UIView) { self.init() addAndContain(view) diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index 88cabc68..0c706be3 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -20,230 +20,228 @@ open class CoreUIModelMapping: ModelMapping { open class func registerMolecules() { // MARK:- Stacks - try? ModelRegistry.register(handler: MoleculeStackView.self, for: StackModel.self) - try? ModelRegistry.register(handler: UnOrderedList.self, for: UnOrderedListModel.self) - try? ModelRegistry.register(handler: NumberedList.self, for: NumberedListModel.self) + ModelRegistry.register(handler: MoleculeStackView.self, for: StackModel.self) + ModelRegistry.register(handler: UnOrderedList.self, for: UnOrderedListModel.self) + ModelRegistry.register(handler: NumberedList.self, for: NumberedListModel.self) // MARK:- Label - try? ModelRegistry.register(handler: Label.self, for: LabelModel.self) + ModelRegistry.register(handler: Label.self, for: LabelModel.self) // MARK:- TextView - try? ModelRegistry.register(handler: TextViewEntryField.self, for: TextViewEntryFieldModel.self) + ModelRegistry.register(handler: TextViewEntryField.self, for: TextViewEntryFieldModel.self) // MARK:- Buttons - try? ModelRegistry.register(handler: PillButton.self, for: ButtonModel.self) - try? ModelRegistry.register(handler: TwoButtonView.self, for: TwoButtonViewModel.self) - try? ModelRegistry.register(handler: ExternalLink.self, for: ExternalLinkModel.self) - try? ModelRegistry.register(handler: Link.self, for: LinkModel.self) - try? ModelRegistry.register(handler: CaretLink.self, for: CaretLinkModel.self) + ModelRegistry.register(handler: PillButton.self, for: ButtonModel.self) + ModelRegistry.register(handler: TwoButtonView.self, for: TwoButtonViewModel.self) + ModelRegistry.register(handler: ExternalLink.self, for: ExternalLinkModel.self) + ModelRegistry.register(handler: Link.self, for: LinkModel.self) + ModelRegistry.register(handler: CaretLink.self, for: CaretLinkModel.self) // MARK:- Entry Field - try? ModelRegistry.register(handler: TextEntryField.self, for: TextEntryFieldModel.self) - try? ModelRegistry.register(handler: MdnEntryField.self, for: MdnEntryFieldModel.self) - try? ModelRegistry.register(handler: DigitEntryField.self, for: DigitEntryFieldModel.self) - try? ModelRegistry.register(handler: ItemDropdownEntryField.self, for: ItemDropdownEntryFieldModel.self) - try? ModelRegistry.register(handler: DateDropdownEntryField.self, for: DateDropdownEntryFieldModel.self) - try? ModelRegistry.register(handler: MultiItemDropdownEntryField.self, for: MultiItemDropdownEntryFieldModel.self) + ModelRegistry.register(handler: TextEntryField.self, for: TextEntryFieldModel.self) + ModelRegistry.register(handler: MdnEntryField.self, for: MdnEntryFieldModel.self) + ModelRegistry.register(handler: DigitEntryField.self, for: DigitEntryFieldModel.self) + ModelRegistry.register(handler: ItemDropdownEntryField.self, for: ItemDropdownEntryFieldModel.self) + ModelRegistry.register(handler: DateDropdownEntryField.self, for: DateDropdownEntryFieldModel.self) + ModelRegistry.register(handler: MultiItemDropdownEntryField.self, for: MultiItemDropdownEntryFieldModel.self) // MARK:- Selectors - try? ModelRegistry.register(handler: RadioButton.self, for: RadioButtonModel.self) - try? ModelRegistry.register(handler: RadioBoxes.self, for: RadioBoxesModel.self) - try? ModelRegistry.register(handler: Checkbox.self, for: CheckboxModel.self) - try? ModelRegistry.register(handler: RadioSwatches.self, for: RadioSwatchesModel.self) - try? ModelRegistry.register(handler: Tags.self, for: TagsModel.self) - try? ModelRegistry.register(handler: Tag.self, for: TagModel.self) - try? ModelRegistry.register(handler: Heart.self, for: HeartModel.self) - try? ModelRegistry.register(handler: Stars.self, for: StarsModel.self) - try? ModelRegistry.register(handler: Star.self, for: StarModel.self) - - + ModelRegistry.register(handler: RadioButton.self, for: RadioButtonModel.self) + ModelRegistry.register(handler: RadioBoxes.self, for: RadioBoxesModel.self) + ModelRegistry.register(handler: Checkbox.self, for: CheckboxModel.self) + ModelRegistry.register(handler: RadioSwatches.self, for: RadioSwatchesModel.self) + ModelRegistry.register(handler: Tags.self, for: TagsModel.self) + ModelRegistry.register(handler: Tag.self, for: TagModel.self) + ModelRegistry.register(handler: Heart.self, for: HeartModel.self) + ModelRegistry.register(handler: Stars.self, for: StarsModel.self) + ModelRegistry.register(handler: Star.self, for: StarModel.self) + // MARK:- Other Atoms - try? ModelRegistry.register(handler: ProgressBar.self, for: ProgressBarModel.self) - try? ModelRegistry.register(handler: MultiProgress.self, for: MultiProgressBarModel.self) - try? ModelRegistry.register(handler: CaretView.self, for: CaretViewModel.self) - try? ModelRegistry.register(handler: DashLine.self, for: DashLineModel.self) - try? ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self) - try? ModelRegistry.register(handler: Line.self, for: LineModel.self) - try? ModelRegistry.register(handler: Wheel.self, for: WheelModel.self) - try? ModelRegistry.register(handler: Toggle.self, for: ToggleModel.self) - try? ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self) - try? ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self) - try? ModelRegistry.register(handler: RadioButtonLabel.self, for: RadioButtonLabelModel.self) - try? ModelRegistry.register(handler: WebView.self, for: WebViewModel.self) - try? ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self) - try? ModelRegistry.register(handler: Video.self, for: VideoModel.self) + ModelRegistry.register(handler: ProgressBar.self, for: ProgressBarModel.self) + ModelRegistry.register(handler: MultiProgress.self, for: MultiProgressBarModel.self) + ModelRegistry.register(handler: CaretView.self, for: CaretViewModel.self) + ModelRegistry.register(handler: DashLine.self, for: DashLineModel.self) + ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self) + ModelRegistry.register(handler: Line.self, for: LineModel.self) + ModelRegistry.register(handler: Wheel.self, for: WheelModel.self) + ModelRegistry.register(handler: Toggle.self, for: ToggleModel.self) + ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self) + ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self) + ModelRegistry.register(handler: RadioButtonLabel.self, for: RadioButtonLabelModel.self) + ModelRegistry.register(handler: WebView.self, for: WebViewModel.self) + ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self) + ModelRegistry.register(handler: Video.self, for: VideoModel.self) // MARK:- Horizontal Combination Molecules - try? ModelRegistry.register(handler: StringAndMoleculeView.self, for: StringAndMoleculeModel.self) - try? ModelRegistry.register(handler: ImageHeadlineBody.self, for: ImageHeadlineBodyModel.self) - try? ModelRegistry.register(handler: Tabs.self, for: TabsModel.self) - try? ModelRegistry.register(handler: TwoLinkView.self, for: TwoLinkViewModel.self) + ModelRegistry.register(handler: StringAndMoleculeView.self, for: StringAndMoleculeModel.self) + ModelRegistry.register(handler: ImageHeadlineBody.self, for: ImageHeadlineBodyModel.self) + ModelRegistry.register(handler: Tabs.self, for: TabsModel.self) + ModelRegistry.register(handler: TwoLinkView.self, for: TwoLinkViewModel.self) // MARK:- Vertical Combination Molecules - try? ModelRegistry.register(handler: HeadlineBody.self, for: HeadlineBodyModel.self) - try? ModelRegistry.register(handler: HeadLineBodyCaretLinkImage.self, for: HeadlineBodyCaretLinkImageModel.self) - try? ModelRegistry.register(handler: EyebrowHeadlineBodyLink.self, for: EyebrowHeadlineBodyLinkModel.self) - try? ModelRegistry.register(handler: HeadlineBodyLink.self, for: HeadlineBodyLinkModel.self) - try? ModelRegistry.register(handler: HeadlineBodyButton.self, for: HeadlineBodyButtonModel.self) - try? ModelRegistry.register(handler: BGImageHeadlineBodyButton.self, for: BGImageHeadlineBodyButtonModel.self) - try? ModelRegistry.register(handler: ThreeHeadlineBodyLink.self, for: ThreeHeadlineBodyLinkModel.self) - + ModelRegistry.register(handler: HeadlineBody.self, for: HeadlineBodyModel.self) + ModelRegistry.register(handler: HeadLineBodyCaretLinkImage.self, for: HeadlineBodyCaretLinkImageModel.self) + ModelRegistry.register(handler: EyebrowHeadlineBodyLink.self, for: EyebrowHeadlineBodyLinkModel.self) + ModelRegistry.register(handler: HeadlineBodyLink.self, for: HeadlineBodyLinkModel.self) + ModelRegistry.register(handler: HeadlineBodyButton.self, for: HeadlineBodyButtonModel.self) + ModelRegistry.register(handler: BGImageHeadlineBodyButton.self, for: BGImageHeadlineBodyButtonModel.self) + ModelRegistry.register(handler: ThreeHeadlineBodyLink.self, for: ThreeHeadlineBodyLinkModel.self) + ModelRegistry.register(handler: ImageButton.self, for: ImageButtonModel.self) + // MARK:- Left Right Molecules - try? ModelRegistry.register(handler: CornerLabels.self, for: CornerLabelsModel.self) - try? ModelRegistry.register(handler: LeftRightLabelView.self, for: LeftRightLabelModel.self) - try? ModelRegistry.register(handler: LabelToggle.self, for: LabelToggleModel.self) - try? ModelRegistry.register(handler: HeadlineBodyToggle.self, for: HeadlineBodyToggleModel.self) - try? ModelRegistry.register(handler: HeadlineBodyLinkToggle.self, for: HeadlineBodyLinkToggleModel.self) - try? ModelRegistry.register(handler: ActionDetailWithImage.self, for: ActionDetailWithImageModel.self) - + ModelRegistry.register(handler: CornerLabels.self, for: CornerLabelsModel.self) + ModelRegistry.register(handler: LeftRightLabelView.self, for: LeftRightLabelModel.self) + ModelRegistry.register(handler: LabelToggle.self, for: LabelToggleModel.self) + ModelRegistry.register(handler: HeadlineBodyToggle.self, for: HeadlineBodyToggleModel.self) + ModelRegistry.register(handler: HeadlineBodyLinkToggle.self, for: HeadlineBodyLinkToggleModel.self) + ModelRegistry.register(handler: ActionDetailWithImage.self, for: ActionDetailWithImageModel.self) + // MARK:- List items - try? ModelRegistry.register(handler: MoleculeTableViewCell.self, for: MoleculeListItemModel.self) - try? ModelRegistry.register(handler: DropDownFilterTableViewCell.self, for: DropDownListItemModel.self) - try? ModelRegistry.register(handler: AccordionMoleculeTableViewCell.self, for: AccordionListItemModel.self) - try? ModelRegistry.register(handler: TabsTableViewCell.self, for: TabsListItemModel.self) - try? ModelRegistry.register(handler: ListProgressBarData.self, for: ListProgressBarDataModel.self) + ModelRegistry.register(handler: MoleculeTableViewCell.self, for: MoleculeListItemModel.self) + ModelRegistry.register(handler: DropDownFilterTableViewCell.self, for: DropDownListItemModel.self) + ModelRegistry.register(handler: AccordionMoleculeTableViewCell.self, for: AccordionListItemModel.self) + ModelRegistry.register(handler: TabsTableViewCell.self, for: TabsListItemModel.self) + ModelRegistry.register(handler: ListProgressBarData.self, for: ListProgressBarDataModel.self) // MARK:- Other Items - try? ModelRegistry.register(handler: MoleculeStackItem.self, for: MoleculeStackItemModel.self) - try? ModelRegistry.register(handler: StackItem.self, for: StackItemModel.self) - try? ModelRegistry.register(handler: MoleculeCollectionViewCell.self, for: MoleculeCollectionItemModel.self) - try? ModelRegistry.register(handler: CarouselItem.self, for: CarouselItemModel.self) - - + ModelRegistry.register(handler: MoleculeStackItem.self, for: MoleculeStackItemModel.self) + ModelRegistry.register(handler: StackItem.self, for: StackItemModel.self) + ModelRegistry.register(handler: MoleculeCollectionViewCell.self, for: MoleculeCollectionItemModel.self) + ModelRegistry.register(handler: CarouselItem.self, for: CarouselItemModel.self) + // MARK:- Other Container Molecules - try? ModelRegistry.register(handler: MoleculeContainer.self, for: MoleculeContainerModel.self) - try? ModelRegistry.register(handler: MoleculeHeaderView.self, for: MoleculeHeaderModel.self) - try? ModelRegistry.register(handler: FooterView.self, for: FooterModel.self) - try? ModelRegistry.register(handler: Scroller.self, for: ScrollerModel.self) - try? ModelRegistry.register(handler: ModuleMolecule.self, for: ModuleMoleculeModel.self) - try? ModelRegistry.register(handler: BGImageMolecule.self, for: BGImageMoleculeModel.self) - try? ModelRegistry.register(handler: BGVideoImageMolecule.self, for: BGVideoImageMoleculeModel.self) - try? ModelRegistry.register(handler: MoleculeSectionHeader.self, for: MoleculeSectionHeaderModel.self) - try? ModelRegistry.register(handler: MoleculeSectionFooter.self, for: MoleculeSectionFooterModel.self) - - + ModelRegistry.register(handler: MoleculeContainer.self, for: MoleculeContainerModel.self) + ModelRegistry.register(handler: MoleculeHeaderView.self, for: MoleculeHeaderModel.self) + ModelRegistry.register(handler: FooterView.self, for: FooterModel.self) + ModelRegistry.register(handler: Scroller.self, for: ScrollerModel.self) + ModelRegistry.register(handler: ModuleMolecule.self, for: ModuleMoleculeModel.self) + ModelRegistry.register(handler: BGImageMolecule.self, for: BGImageMoleculeModel.self) + ModelRegistry.register(handler: BGVideoImageMolecule.self, for: BGVideoImageMoleculeModel.self) + ModelRegistry.register(handler: MoleculeSectionHeader.self, for: MoleculeSectionHeaderModel.self) + ModelRegistry.register(handler: MoleculeSectionFooter.self, for: MoleculeSectionFooterModel.self) + // MARK:- Other Molecules - try? ModelRegistry.register(handler: DoughnutChartView.self, for: DoughnutChartModel.self) + ModelRegistry.register(handler: DoughnutChartView.self, for: DoughnutChartModel.self) // Navigation Molecules - try? ModelRegistry.register(NavigationItemModel.self) - try? ModelRegistry.register(NavigationImageButtonModel.self) - try? ModelRegistry.register(NavigationLabelButtonModel.self) + ModelRegistry.register(NavigationItemModel.self) + ModelRegistry.register(NavigationImageButtonModel.self) + ModelRegistry.register(NavigationLabelButtonModel.self) // MARK:- Other Organisms - try? ModelRegistry.register(handler: Carousel.self, for: CarouselModel.self) - try? ModelRegistry.register(handler: BarsIndicatorView.self, for: BarsCarouselIndicatorModel.self) - try? ModelRegistry.register(handler: NumericIndicatorView.self, for: NumericCarouselIndicatorModel.self) + ModelRegistry.register(handler: Carousel.self, for: CarouselModel.self) + ModelRegistry.register(handler: BarsIndicatorView.self, for: BarsCarouselIndicatorModel.self) + ModelRegistry.register(handler: NumericIndicatorView.self, for: NumericCarouselIndicatorModel.self) // MARK:- Designed List Items - try? ModelRegistry.register(handler: ListLeftVariableIconWithRightCaret.self, for: ListLeftVariableIconWithRightCaretModel.self) - try? ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretBodyText.self, for: ListLeftVariableIconWithRightCaretBodyTextModel.self) - try? ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretAllTextLinks.self, for: ListLeftVariableIconWithRightCaretAllTextLinksModel.self) - try? ModelRegistry.register(handler: ListLeftVariableCheckboxAllTextAndLinks.self, for: ListLeftVariableCheckboxAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListLeftVariableRadioButtonAndPaymentMethod.self, for: ListLeftVariableRadioButtonAndPaymentMethodModel.self) - try? ModelRegistry.register(handler: ListLeftVariableRadioButtonBodyText.self, for: ListLeftVariableRadioButtonBodyTextModel.self) - try? ModelRegistry.register(handler: ListLeftVariableRadioButtonAllTextAndLinks.self, for: ListLeftVariableRadioButtonAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListLeftVariableCheckboxBodyText.self, for: ListLeftVariableCheckboxBodyTextModel.self) - try? ModelRegistry.register(handler: ListLeftVariableIconAllTextLinks.self, for: ListLeftVariableIconAllTextLinksModel.self) - try? ModelRegistry.register(handler: ListLeftVariableNumberedListAllTextAndLinks.self, for: ListLeftVariableNumberedListAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListLeftVariableNumberedListBodyText.self, for: ListLeftVariableNumberedListBodyTextModel.self) - try? ModelRegistry.register(handler: ListRVWheel.self, for: ListRVWheelModel.self) - try? ModelRegistry.register(handler: ListRightVariablePayments.self, for: ListRightVariablePaymentsModel.self) - try? ModelRegistry.register(handler: ListRightVariableTotalData.self, for: ListRightVariableTotalDataModel.self) - try? ModelRegistry.register(handler: ListRightVariableTextLinkAllTextAndLinks.self, for: ListRightVariableTextLinkAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListRightVariableButtonAllTextAndLinks.self, for: ListRightVariableButtonAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListRightVariablePriceChangeBodyText.self, for: ListRightVariablePriceChangeBodyTextModel.self) - try? ModelRegistry.register(handler: ListRightVariablePriceChangeAllTextAndLinks.self, for: ListRightVariablePriceChangeAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListRightVariableToggleAllTextAndLinks.self, for: ListRightVariableToggleAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListRightVariableRightCaretAllTextAndLinks.self, for: ListRightVariableRightCaretAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListOneColumnFullWidthTextAllTextAndLinks.self, for: ListOneColumnFullWidthTextAllTextAndLinksModel.self) - try? ModelRegistry.register(handler: ListOneColumnFullWidthTextBodyText.self, for: ListOneColumnFullWidthTextBodyTextModel.self) - try? ModelRegistry.register(handler: ListTwoColumnCompareChanges.self, for: ListTwoColumnCompareChangesModel.self) - try? ModelRegistry.register(handler: ListTwoColumnPriceDetails.self, for: ListTwoColumnPriceDetailsModel.self) - try? ModelRegistry.register(handler: ListTwoColumnPriceDescription.self, for: ListTwoColumnPriceDescriptionModel.self) - try? ModelRegistry.register(handler: ListTwoColumnDropdownSelectors.self, for: ListTwoColumnDropdownSelectorsModel.self) - try? ModelRegistry.register(handler: ListThreeColumnInternationalData.self, for: ListThreeColumnInternationalDataModel.self) - try? ModelRegistry.register(handler: ListThreeColumnDataUsage.self, for: ListThreeColumnDataUsageModel.self) - try? ModelRegistry.register(handler: ListThreeColumnBillChanges.self, for: ListThreeColumnBillChangesModel.self) - try? ModelRegistry.register(handler: ListThreeColumnBillHistory.self, for: ListThreeColumnBillHistoryModel.self) - try? ModelRegistry.register(handler: ListThreeColumnSpeedTest.self, for: ListThreeColumnSpeedTestModel.self) - try? ModelRegistry.register(handler: ListFourColumnDataUsageListItem.self, for: ListFourColumnDataUsageListItemModel.self) - try? ModelRegistry.register(handler: ListProgressBarThin.self, for: ListProgressBarThinModel.self) - try? ModelRegistry.register(handler: ListStoreLocator.self, for: ListStoreLocatorModel.self) - try? ModelRegistry.register(handler: ListStarRating.self, for: ListStarRatingModel.self) + ModelRegistry.register(handler: ListLeftVariableIconWithRightCaret.self, for: ListLeftVariableIconWithRightCaretModel.self) + ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretBodyText.self, for: ListLeftVariableIconWithRightCaretBodyTextModel.self) + ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretAllTextLinks.self, for: ListLeftVariableIconWithRightCaretAllTextLinksModel.self) + ModelRegistry.register(handler: ListLeftVariableCheckboxAllTextAndLinks.self, for: ListLeftVariableCheckboxAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListLeftVariableRadioButtonAndPaymentMethod.self, for: ListLeftVariableRadioButtonAndPaymentMethodModel.self) + ModelRegistry.register(handler: ListLeftVariableRadioButtonBodyText.self, for: ListLeftVariableRadioButtonBodyTextModel.self) + ModelRegistry.register(handler: ListLeftVariableRadioButtonAllTextAndLinks.self, for: ListLeftVariableRadioButtonAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListLeftVariableCheckboxBodyText.self, for: ListLeftVariableCheckboxBodyTextModel.self) + ModelRegistry.register(handler: ListLeftVariableIconAllTextLinks.self, for: ListLeftVariableIconAllTextLinksModel.self) + ModelRegistry.register(handler: ListLeftVariableNumberedListAllTextAndLinks.self, for: ListLeftVariableNumberedListAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListLeftVariableNumberedListBodyText.self, for: ListLeftVariableNumberedListBodyTextModel.self) + ModelRegistry.register(handler: ListRVWheel.self, for: ListRVWheelModel.self) + ModelRegistry.register(handler: ListRightVariablePayments.self, for: ListRightVariablePaymentsModel.self) + ModelRegistry.register(handler: ListRightVariableTotalData.self, for: ListRightVariableTotalDataModel.self) + ModelRegistry.register(handler: ListRightVariableTextLinkAllTextAndLinks.self, for: ListRightVariableTextLinkAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListRightVariableButtonAllTextAndLinks.self, for: ListRightVariableButtonAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListRightVariablePriceChangeBodyText.self, for: ListRightVariablePriceChangeBodyTextModel.self) + ModelRegistry.register(handler: ListRightVariablePriceChangeAllTextAndLinks.self, for: ListRightVariablePriceChangeAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListRightVariableToggleAllTextAndLinks.self, for: ListRightVariableToggleAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListRightVariableRightCaretAllTextAndLinks.self, for: ListRightVariableRightCaretAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListOneColumnFullWidthTextAllTextAndLinks.self, for: ListOneColumnFullWidthTextAllTextAndLinksModel.self) + ModelRegistry.register(handler: ListOneColumnFullWidthTextBodyText.self, for: ListOneColumnFullWidthTextBodyTextModel.self) + ModelRegistry.register(handler: ListTwoColumnCompareChanges.self, for: ListTwoColumnCompareChangesModel.self) + ModelRegistry.register(handler: ListTwoColumnPriceDetails.self, for: ListTwoColumnPriceDetailsModel.self) + ModelRegistry.register(handler: ListTwoColumnPriceDescription.self, for: ListTwoColumnPriceDescriptionModel.self) + ModelRegistry.register(handler: ListTwoColumnDropdownSelectors.self, for: ListTwoColumnDropdownSelectorsModel.self) + ModelRegistry.register(handler: ListThreeColumnInternationalData.self, for: ListThreeColumnInternationalDataModel.self) + ModelRegistry.register(handler: ListThreeColumnDataUsage.self, for: ListThreeColumnDataUsageModel.self) + ModelRegistry.register(handler: ListThreeColumnBillChanges.self, for: ListThreeColumnBillChangesModel.self) + ModelRegistry.register(handler: ListThreeColumnBillHistory.self, for: ListThreeColumnBillHistoryModel.self) + ModelRegistry.register(handler: ListThreeColumnSpeedTest.self, for: ListThreeColumnSpeedTestModel.self) + ModelRegistry.register(handler: ListFourColumnDataUsageListItem.self, for: ListFourColumnDataUsageListItemModel.self) + ModelRegistry.register(handler: ListProgressBarThin.self, for: ListProgressBarThinModel.self) + ModelRegistry.register(handler: ListStoreLocator.self, for: ListStoreLocatorModel.self) + ModelRegistry.register(handler: ListStarRating.self, for: ListStarRatingModel.self) // MARK:- Designed Section Dividers - try? ModelRegistry.register(handler: ListFourColumnDataUsageDivider.self, for: ListFourColumnDataUsageDividerModel.self) - try? ModelRegistry.register(handler: ListThreeColumnPlanDataDivider.self, for: ListThreeColumnPlanDataDividerModel.self) - try? ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerShort.self, for: ListOneColumnTextWithWhitespaceDividerShortModel.self) - try? ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerTall.self, for: ListOneColumnTextWithWhitespaceDividerTallModel.self) - try? ModelRegistry.register(handler: ListOneColumnFullWidthTextDividerSubsection.self, for: ListOneColumnFullWidthTextDividerSubsectionModel.self) - try? ModelRegistry.register(handler: ListTwoColumnSubsectionDivider.self, for: ListTwoColumnSubsectionDividerModel.self) - try? ModelRegistry.register(handler: ListThreeColumnInternationalDataDivider.self, for: ListThreeColumnInternationalDataDividerModel.self) - try? ModelRegistry.register(handler: ListThreeColumnSpeedTestDivider.self, for: ListThreeColumnSpeedTestDividerModel.self) - try? ModelRegistry.register(handler: ListThreeColumnBillChangesDivider.self, for: ListThreeColumnBillChangesDividerModel.self) - try? ModelRegistry.register(handler: ListThreeColumnDataUsageDivider.self, for: ListThreeColumnDataUsageDividerModel.self) - try? ModelRegistry.register(handler: ListThreeColumnBillHistoryDivider.self, for: ListThreeColumnBillHistoryDividerModel.self) - + ModelRegistry.register(handler: ListFourColumnDataUsageDivider.self, for: ListFourColumnDataUsageDividerModel.self) + ModelRegistry.register(handler: ListThreeColumnPlanDataDivider.self, for: ListThreeColumnPlanDataDividerModel.self) + ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerShort.self, for: ListOneColumnTextWithWhitespaceDividerShortModel.self) + ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerTall.self, for: ListOneColumnTextWithWhitespaceDividerTallModel.self) + ModelRegistry.register(handler: ListOneColumnFullWidthTextDividerSubsection.self, for: ListOneColumnFullWidthTextDividerSubsectionModel.self) + ModelRegistry.register(handler: ListTwoColumnSubsectionDivider.self, for: ListTwoColumnSubsectionDividerModel.self) + ModelRegistry.register(handler: ListThreeColumnInternationalDataDivider.self, for: ListThreeColumnInternationalDataDividerModel.self) + ModelRegistry.register(handler: ListThreeColumnSpeedTestDivider.self, for: ListThreeColumnSpeedTestDividerModel.self) + ModelRegistry.register(handler: ListThreeColumnBillChangesDivider.self, for: ListThreeColumnBillChangesDividerModel.self) + ModelRegistry.register(handler: ListThreeColumnDataUsageDivider.self, for: ListThreeColumnDataUsageDividerModel.self) + ModelRegistry.register(handler: ListThreeColumnBillHistoryDivider.self, for: ListThreeColumnBillHistoryDividerModel.self) + // MARK:- Designed Headers - try? ModelRegistry.register(handler: HeadersH1Button.self, for: HeadersH1ButtonModel.self) - try? ModelRegistry.register(handler: HeadersH1LandingPageHeader.self, for: HeadersH1LandingPageHeaderModel.self) - try? ModelRegistry.register(handler: HeadersH1NoButtonsBodyText.self, for: HeadersH1NoButtonsBodyTextModel.self) - try? ModelRegistry.register(handler: HeadersH2NoButtonsBodyText.self, for: HeadersH2NoButtonsBodyTextModel.self) - try? ModelRegistry.register(handler: HeadersH2TinyButton.self, for: HeadersH2TinyButtonModel.self) - try? ModelRegistry.register(handler: HeadersH2Buttons.self, for: HeadersH2ButtonsModel.self) - try? ModelRegistry.register(handler: HeadersH2PricingTwoRows.self, for: HeadersH2PricingTwoRowsModel.self) - try? ModelRegistry.register(handler: HeadersH2Link.self, for: HeadersH2LinkModel.self) - try? ModelRegistry.register(handler: HeadersH2CaretLink.self, for: HeadersH2CaretLinkModel.self) + ModelRegistry.register(handler: HeadersH1Button.self, for: HeadersH1ButtonModel.self) + ModelRegistry.register(handler: HeadersH1LandingPageHeader.self, for: HeadersH1LandingPageHeaderModel.self) + ModelRegistry.register(handler: HeadersH1NoButtonsBodyText.self, for: HeadersH1NoButtonsBodyTextModel.self) + ModelRegistry.register(handler: HeadersH2NoButtonsBodyText.self, for: HeadersH2NoButtonsBodyTextModel.self) + ModelRegistry.register(handler: HeadersH2TinyButton.self, for: HeadersH2TinyButtonModel.self) + ModelRegistry.register(handler: HeadersH2Buttons.self, for: HeadersH2ButtonsModel.self) + ModelRegistry.register(handler: HeadersH2PricingTwoRows.self, for: HeadersH2PricingTwoRowsModel.self) + ModelRegistry.register(handler: HeadersH2Link.self, for: HeadersH2LinkModel.self) + ModelRegistry.register(handler: HeadersH2CaretLink.self, for: HeadersH2CaretLinkModel.self) // MARK:- Device Items - try? ModelRegistry.register(handler: ListDeviceComplexButtonMedium.self, for: ListDeviceComplexButtonMediumModel.self) - try? ModelRegistry.register(handler: ListDeviceComplexButtonSmall.self, for: ListDeviceComplexButtonSmallModel.self) - - try? ModelRegistry.register(handler: ListDeviceComplexLinkSmall.self, for: ListDeviceComplexLinkSmallModel.self) - try? ModelRegistry.register(handler: ListDeviceComplexLinkMedium.self, for: ListDeviceComplexLinkMediumModel.self) + ModelRegistry.register(handler: ListDeviceComplexButtonMedium.self, for: ListDeviceComplexButtonMediumModel.self) + ModelRegistry.register(handler: ListDeviceComplexButtonSmall.self, for: ListDeviceComplexButtonSmallModel.self) + + ModelRegistry.register(handler: ListDeviceComplexLinkSmall.self, for: ListDeviceComplexLinkSmallModel.self) + ModelRegistry.register(handler: ListDeviceComplexLinkMedium.self, for: ListDeviceComplexLinkMediumModel.self) // MARK:- LockUps - try? ModelRegistry.register(handler: LockUpsPlanNames.self, for: LockUpsPlanNamesModel.self) - try? ModelRegistry.register(handler: LockupsPlanSMLXL.self, for: LockupsPlanSMLXLModel.self) - + ModelRegistry.register(handler: LockUpsPlanNames.self, for: LockUpsPlanNamesModel.self) + ModelRegistry.register(handler: LockupsPlanSMLXL.self, for: LockupsPlanSMLXLModel.self) + // MARK: - Top Notifications - try? ModelRegistry.register(handler: NotificationView.self, for: NotificationModel.self) - try? ModelRegistry.register(handler: CollapsableNotification.self, for: CollapsableNotificationModel.self) + ModelRegistry.register(handler: NotificationView.self, for: NotificationModel.self) + ModelRegistry.register(handler: CollapsableNotification.self, for: CollapsableNotificationModel.self) } open class func registerLabelAttributes() { - try? ModelRegistry.register(LabelAttributeFontModel.self) - try? ModelRegistry.register(LabelAttributeColorModel.self) - try? ModelRegistry.register(LabelAttributeImageModel.self) - try? ModelRegistry.register(LabelAttributeUnderlineModel.self) - try? ModelRegistry.register(LabelAttributeStrikeThroughModel.self) - try? ModelRegistry.register(LabelAttributeActionModel.self) + ModelRegistry.register(LabelAttributeFontModel.self) + ModelRegistry.register(LabelAttributeColorModel.self) + ModelRegistry.register(LabelAttributeImageModel.self) + ModelRegistry.register(LabelAttributeUnderlineModel.self) + ModelRegistry.register(LabelAttributeStrikeThroughModel.self) + ModelRegistry.register(LabelAttributeActionModel.self) } open class func registerBehaviors() { - try? ModelRegistry.register(handler: ScreenBrightnessModifierBehavior.self, for: ScreenBrightnessModifierBehaviorModel.self) - try? ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self) - try? ModelRegistry.register(handler: AddRemoveMoleculeBehavior.self, for: AddRemoveMoleculeBehaviorModel.self) + ModelRegistry.register(handler: ScreenBrightnessModifierBehavior.self, for: ScreenBrightnessModifierBehaviorModel.self) + ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self) + ModelRegistry.register(handler: AddRemoveMoleculeBehavior.self, for: AddRemoveMoleculeBehaviorModel.self) } - + open override class func registerActions() { super.registerActions() - try? ModelRegistry.register(ActionPopupModel.self) - try? ModelRegistry.register(ActionAlertModel.self) - try? ModelRegistry.register(ActionTopAlertModel.self) - try? ModelRegistry.register(ActionCollapseNotificationModel.self) - try? ModelRegistry.register(ActionOpenPanelModel.self) - try? ModelRegistry.register(ActionTopNotificationModel.self) + ModelRegistry.register(ActionPopupModel.self) + ModelRegistry.register(ActionAlertModel.self) + ModelRegistry.register(ActionTopAlertModel.self) + ModelRegistry.register(ActionCollapseNotificationModel.self) + ModelRegistry.register(ActionOpenPanelModel.self) + ModelRegistry.register(ActionTopNotificationModel.self) } open class func registerRules() { - try? ModelRegistry.register(RuleRequiredModel.self) - try? ModelRegistry.register(RuleAnyRequiredModel.self) - try? ModelRegistry.register(RuleAnyValueChangedModel.self) - try? ModelRegistry.register(RuleAllValueChangedModel.self) - try? ModelRegistry.register(RuleEqualsModel.self) - try? ModelRegistry.register(RuleEqualsIgnoreCaseModel.self) - try? ModelRegistry.register(RuleRegexModel.self) + ModelRegistry.register(RuleRequiredModel.self) + ModelRegistry.register(RuleAnyRequiredModel.self) + ModelRegistry.register(RuleAnyValueChangedModel.self) + ModelRegistry.register(RuleAllValueChangedModel.self) + ModelRegistry.register(RuleEqualsModel.self) + ModelRegistry.register(RuleEqualsIgnoreCaseModel.self) + ModelRegistry.register(RuleRegexModel.self) } } diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.h b/MVMCoreUI/Utility/MVMCoreUIUtility.h index 6852e936..c6959723 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.h +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.h @@ -32,7 +32,10 @@ NS_ASSUME_NONNULL_BEGIN + (UIEdgeInsets)getMarginsForView:(nullable UIView *)view; /// Gets the current visible view controller. Checks presented view controllers first, and then it checks on the NavigationController in the session object. -+ (UIViewController *)getCurrentVisibleController; ++ (nullable UIViewController *)getCurrentVisibleController; + +/// Gets the first non manager controller. ++ (nullable UIViewController *)getViewControllerTraversingManagers:(UIViewController *)viewController; /// Checks if the view or any descendents of the view is currently focused for voice over. + (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view; diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index dd059fed..01f576c5 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -63,14 +63,19 @@ } // if it is not presented viewcontroller, existing BAU logic will be working if (!viewController) { - viewController = [MVMCoreUISession sharedGlobal].navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + viewController = [self getViewControllerTraversingManagers:[MVMCoreUISession sharedGlobal].navigationController.topViewController]; } return viewController; } ++ (UIViewController *)getViewControllerTraversingManagers:(UIViewController *)viewController { + UIViewController *controller = viewController; + while ([controller conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { + controller = [controller performSelector:@selector(getCurrentViewController)]; + } + return controller; +} + + (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view { if (!UIAccessibilityIsVoiceOverRunning()) { return NO; diff --git a/Scripts/build_aggregate.sh b/Scripts/build_aggregate.sh new file mode 100755 index 00000000..514d0075 --- /dev/null +++ b/Scripts/build_aggregate.sh @@ -0,0 +1,31 @@ +unset TOOLCHAINS #Xcode 7.3 BUG FIX http://stackoverflow.com/questions/36184930/xcodebuild-7-3-cant-enable-bitcode + +# define output folder environment variable +C_PROJECT_NAME="MVMCoreUI" +PHONE_CONFIGURATION="Release" +SIMULATOR_CONFIGURATION="Debug" +BUILD_DIR=$(xcodebuild -showBuildSettings -project ./MVMCoreUI.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-) +SIMULATOR_LIBRARY_PATH="${BUILD_DIR}/${SIMULATOR_CONFIGURATION}-iphonesimulator/${C_PROJECT_NAME}.framework" +FRAMEWORKS_DIR=$BUILD_DIR/Frameworks +UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/universal + +# Step 1. Build Device and Simulator versions +xcodebuild -scheme "${C_PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${PHONE_CONFIGURATION} -sdk iphoneos -archivePath "${BUILD_DIR}/${PHONE_CONFIGURATION}-iphoneos/${C_PROJECT_NAME}" archive SKIP_INSTALL=false FRAMEWORK_SEARCH_PATHS=$FRAMEWORKS_DIR ALWAYS_SEARCH_USER_PATHS=true + +xcodebuild -target "${C_PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${SIMULATOR_CONFIGURATION} -sdk iphonesimulator BUILD_DIR=$BUILD_DIR FRAMEWORK_SEARCH_PATHS=$FRAMEWORKS_DIR ALWAYS_SEARCH_USER_PATHS=true ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=true + +mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" + +rm -rf ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework +cp -R "${BUILD_DIR}/${PHONE_CONFIGURATION}-iphoneos/${C_PROJECT_NAME}.xcarchive/Products/Library/Frameworks/${C_PROJECT_NAME}.framework" ${UNIVERSAL_OUTPUTFOLDER} + +# Step 2. Create universal binary file using lipo + +lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}" "${SIMULATOR_LIBRARY_PATH}/${C_PROJECT_NAME}" + +mv ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME} ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME} + +# For Swift framework, Swiftmodule needs to be copied in the universal framework +if [ -d "${SIMULATOR_LIBRARY_PATH}/Modules/${C_PROJECT_NAME}.swiftmodule/" ]; then + cp -a "${SIMULATOR_LIBRARY_PATH}/Modules/${C_PROJECT_NAME}.swiftmodule/" "${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/Modules/${C_PROJECT_NAME}.swiftmodule/" +fi diff --git a/Scripts/download_dependencies.sh b/Scripts/download_dependencies.sh new file mode 100755 index 00000000..f03a25e8 --- /dev/null +++ b/Scripts/download_dependencies.sh @@ -0,0 +1,23 @@ +#!/bin/sh -e + +# download_dependencies.sh +# +# Downloads all compiled framework flavors in from Artifactory. +# + +# Create new aggregate builds + +if [ -z $ARTIFACTORY_URL ]; then + ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory" +fi + +BUILD_DIR=$(xcodebuild -showBuildSettings -project ./MVMCoreUI.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-) +FRAMEWORKS_DIR=$BUILD_DIR/Frameworks + +if [ ! -d $FRAMEWORKS_DIR ]; then + mkdir $FRAMEWORKS_DIR +fi + +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/MVMCore.framework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/MVMCore/1.0/MVMCore-1.0-Debug-SNAPSHOT.zip + +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/MVMAnimationFramework.framework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/MVMAnimationFramework.framework/1.9/MVMAnimationFramework.framework-1.9.zip diff --git a/Scripts/download_framework.sh b/Scripts/download_framework.sh new file mode 100755 index 00000000..9e9e2bb5 --- /dev/null +++ b/Scripts/download_framework.sh @@ -0,0 +1,86 @@ +#!/bin/bash -e + +# upload_framework.sh +# +# Downloads an iOS framework from Artificatory. +# +# An API key from Artifcatory is required in the api_key.private file before uploading. +# + +URL=$1 +LOCALPATH="${2}" +REMOTEPATH="${3}" +LOGFILE=$3 +LOCALDIR=$(dirname "${LOCALPATH}") +LOCALBASE=$(basename "${LOCALPATH}") +NEWFILEPATH="${LOCALDIR}"/$(basename "${REMOTEPATH}") +VERSIONFILE=$LOCALDIR/../Checksums/"${LOCALBASE}".txt + +if [ -z $URL ]; then + echo "The artifactory instance url must be specified as the first argument!" + exit 1 +fi + +#first argument is error message. +exit_with_error () { +echo "Error: $1" +if [ -f "${NEWFILEPATH}" ]; then + rm -rf "${NEWFILEPATH}" 2 +fi +exit 1 +} + +echo "----------------------------------------------------------" +echo "Logs for ${LOCALBASE}" + +echo -e "Local Target: ${LOCALPATH}" +echo -e "Remote Source: ${REMOTEPATH}" + +if [ -z "$LOCALPATH" ]; then + exit_with_error "Missing local path argument" +fi + +if [ -z "$REMOTEPATH" ]; then + exit_with_error "Missing filename path argument" +fi + +#get local and remote checksums for comparison +echo -e "Getting checksums..." +echo -e "URL: ${URL}/api/storage/${REMOTEPATH}" +JSON=$(curl --header "X-JFrog-Art-Api: ${ARTIFACTORY_APIKEY}" -X GET "${URL}/api/storage/${REMOTEPATH}") +CHECKSUM=$(echo "$JSON" | python -c 'import sys, json; print json.load(sys.stdin)["checksums"]["sha1"]') +if [[ -z "$CHECKSUM" ]]; then + exit_with_error "No Checksum found in json: ${JSON}" +fi +echo "Remote checksum ${CHECKSUM}" +if [[ -f "${VERSIONFILE}" ]]; then + OLDCHECKSUM=$(cat "${VERSIONFILE}") +else + OLDCHECKSUM="none" + mkdir -p $(dirname ${VERSIONFILE}) && touch "$VERSIONFILE" +fi +echo "Local checksum ${OLDCHECKSUM}" + +#get new framework if no original framework, no local checksum, or remote checksum is different from local. +if [ ! -e "${LOCALPATH}" ] || [ -z "$OLDCHECKSUM" ] || [ "$CHECKSUM" != "$OLDCHECKSUM" ]; then + echo "Downloading..." + echo -e "URL: ${URL}/${REMOTEPATH}" + curl --header "X-JFrog-Art-Api: ${ARTIFACTORY_APIKEY}" -f -X GET "$URL/$REMOTEPATH" --output "${NEWFILEPATH}" + if [ $? -eq 0 ] && [ -e "${NEWFILEPATH}" ]; then + echo "Finished Downloading, begin unzip" + unzip -q -o "${NEWFILEPATH}" -d "${LOCALDIR}" + if [ $? -eq 0 ]; then + echo "Finished unzipping, remove zip" + rm -rf "${NEWFILEPATH}" + echo "Writing new checksum to file" + echo "${CHECKSUM}" > "${VERSIONFILE}" + echo "Successfully downloaded and unzipped archive." + else + exit_with_error "Error unzipping" + fi + else + exit_with_error "Failed to download" + fi +else + echo "Successful, No New Version" +fi diff --git a/Scripts/upload_core_ui_frameworks.sh b/Scripts/upload_core_ui_frameworks.sh new file mode 100755 index 00000000..4a16396c --- /dev/null +++ b/Scripts/upload_core_ui_frameworks.sh @@ -0,0 +1,24 @@ +#!/bin/sh -e + +# upload_core_ui_frameworks.sh +# +# Uploads all compiled framework flavors in MVMCoreUI to Artifactory with the SNAPSHOT classifier. This is to avoid accidently clobbering a release build of a particular version. Remove the classifier in Artificatory to make a release. +# +# Copied from Hedden, Kyle Matthew on 3/2/18. +# + +FRAMEWORK_VERSION=$(cd .. && agvtool vers -terse) +if [ $(git tag --list | grep "v${FRAMEWORK_VERSION}") ]; then + echo This version tag has already been committed! Aborting! + exit 1 +fi + +# Create new aggregate builds + +if [ -z $ARTIFACTORY_URL ]; then + ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory" +fi + +# Upload +BUILD_DIR=$(xcodebuild -showBuildSettings -project ../MVMCoreUI.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-) +./upload_framework.sh $ARTIFACTORY_URL "${BUILD_DIR}/universal/MVMCoreUI.framework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/MVMCoreUI/[VER]/MVMCoreUI-[VER]-Debug-SNAPSHOT diff --git a/Scripts/upload_framework.sh b/Scripts/upload_framework.sh new file mode 100755 index 00000000..305a6585 --- /dev/null +++ b/Scripts/upload_framework.sh @@ -0,0 +1,89 @@ +#!/bin/bash -e + +# upload_framework.sh +# +# Uploads an iOS framework to Artificatory given the local path as the first argument and the remote project name in Verizon OneArtifactory for the second argument. +# +# An API key from Artifcatory is required in the api_key.private file before uploading. +# +# The script will replace [VER] in the provided remote path with the version found in the framework bundle. +# +# Copied from Hedden, Kyle Matthew on 3/2/18. +# Copyright © 2018 Verizon. All rights reserved. + +URL=$1 +LOCALPATH=$2 +REMOTEPATH=$3 + +if [ -z $URL ]; then + echo "The artifactory instance url must be specified as the first argument!" + exit 1 +fi + +echo ">>> UPLOAD START <<<" +echo "Local path: ${LOCALPATH}" +echo "Remote path: ${REMOTEPATH}" + +cat "${LOCALPATH}/Info.plist" + +LOCALBASE=$(basename "${LOCALPATH}") +LOCALDIR=$(dirname "${LOCALPATH}") + +# Grab the framework version from the bundled Info.plist. +FRAMEWORKVER=$(/usr/libexec/plistbuddy -c "Print :CFBundleShortVersionString" "${LOCALPATH}/Info.plist") +echo -e "\nFramework version: \t${FRAMEWORKVER}" + +# Replace the [VER] placeholders with the found version. +REMOTEPATH="${REMOTEPATH//\[VER\]/$FRAMEWORKVER}" +echo -e "Resolved path: \t\t${REMOTEPATH}" + +if [ -z $ARTIFACTORY_APIKEY ]; then + # Read the API key from a private file. + read -r APIKEY < "api_key.private" +else + APIKEY=$ARTIFACTORY_APIKEY +fi + +if [ -z $APIKEY ]; then + read -p "Artifactory API Key:" APIKEY + echo $APIKEY >> api_key.private +fi + +echo -e "API Key: \t\t${APIKEY}" + +# Zip the framework & DSYM for uploading. +pushd $LOCALDIR +echo -e "---------\nZipping: \t\t${LOCALBASE}.zip" +zip -r -X "${LOCALBASE}.zip" $LOCALBASE +# Generate framework's SHA-1 checksum. +CHECKSUM=$(shasum -a 1 "${LOCALBASE}.zip" | cut -d " " -f 1) +echo -e "SHA-1 Checksum: \t${CHECKSUM}" +if [ -e ${LOCALBASE}.dSYM ]; then + echo -e "---------\nZipping: \t\t${LOCALBASE}.dSYM.zip" + zip -r -X "${LOCALBASE}.dSYM.zip" $LOCALBASE.dSYM + # Generate its SHA-1 checksum for dsym. + DSYM_CHECKSUM=$(shasum -a 1 "${LOCALBASE}.dSYM.zip" | cut -d " " -f 1) + echo -e "SHA-1 Checksum: \t${DSYM_CHECKSUM}" +fi +popd +mv ${LOCALPATH}.zip . +if [ -e ${LOCALPATH}.dSYM.zip ]; then + mv ${LOCALPATH}.dSYM.zip . +fi + +# Upload framework to Artifactory. +echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.zip" +curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.zip" -T "${LOCALBASE}.zip" + +# Cleanup. +rm "${LOCALBASE}.zip" + +if [ -e ${LOCALBASE}.dSYM.zip ]; then + # Upload dsym Artifactory. + echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.dSYM.zip" + curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${DSYM_CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.dSYM.zip" -T "${LOCALBASE}.dSYM.zip" + # Cleanup dsym. + rm ${LOCALBASE}.dSYM.zip +fi + +echo -e "\n\n<<< UPLOAD COMPLETE >>>\n\n"