This commit is contained in:
Pfeil, Scott Robert 2021-07-26 18:11:36 -04:00
commit e853647367
70 changed files with 1265 additions and 473 deletions

90
.gitlab-ci.yml Normal file
View File

@ -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

View File

@ -121,6 +121,8 @@
0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */; }; 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */; };
0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB423FF18D2004C5109 /* Arrow.swift */; }; 0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB423FF18D2004C5109 /* Arrow.swift */; };
0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB623FF18E9004C5109 /* ArrowModel.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 */; }; 279B1569242BBC2F00921D6C /* ActionModelAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */; };
27F6B08826051831008529AA /* MoleculeTreeTraversalProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F6B08726051831008529AA /* MoleculeTreeTraversalProtocol.swift */; }; 27F6B08826051831008529AA /* MoleculeTreeTraversalProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F6B08726051831008529AA /* MoleculeTreeTraversalProtocol.swift */; };
27F6B08C26052AFF008529AA /* ParentMoleculeModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F6B08B26052AFF008529AA /* ParentMoleculeModelProtocol.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 */; }; D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; };
D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.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 */; }; 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 */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; };
D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; };
D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2169300251E51E7002A6324 /* SectionListTemplate.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 = "<group>"; }; 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLinkModel.swift; sourceTree = "<group>"; };
0AE98BB423FF18D2004C5109 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = "<group>"; }; 0AE98BB423FF18D2004C5109 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = "<group>"; };
0AE98BB623FF18E9004C5109 /* ArrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowModel.swift; sourceTree = "<group>"; }; 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowModel.swift; sourceTree = "<group>"; };
1D6D258626899B0B00DEBB08 /* ImageButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageButtonModel.swift; path = MVMCoreUI/Atomic/Atoms/Buttons/ImageButtonModel.swift; sourceTree = SOURCE_ROOT; };
1D6D258726899B0B00DEBB08 /* ImageButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageButton.swift; path = MVMCoreUI/Atomic/Atoms/Buttons/ImageButton.swift; sourceTree = SOURCE_ROOT; };
279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionModelAdapter.swift; sourceTree = "<group>"; }; 279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionModelAdapter.swift; sourceTree = "<group>"; };
27F6B08726051831008529AA /* MoleculeTreeTraversalProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTreeTraversalProtocol.swift; sourceTree = "<group>"; }; 27F6B08726051831008529AA /* MoleculeTreeTraversalProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTreeTraversalProtocol.swift; sourceTree = "<group>"; };
27F6B08B26052AFF008529AA /* ParentMoleculeModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentMoleculeModelProtocol.swift; sourceTree = "<group>"; }; 27F6B08B26052AFF008529AA /* ParentMoleculeModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentMoleculeModelProtocol.swift; sourceTree = "<group>"; };
@ -889,7 +892,6 @@
D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = "<group>"; }; D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = "<group>"; };
D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = "<group>"; }; D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = "<group>"; };
D20F3B43252E00E4004B3F56 /* PageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageProtocol.swift; sourceTree = "<group>"; }; D20F3B43252E00E4004B3F56 /* PageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageProtocol.swift; sourceTree = "<group>"; };
D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarRefreshProtocol.swift; sourceTree = "<group>"; };
D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = "<group>"; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = "<group>"; };
D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
D2169300251E51E7002A6324 /* SectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplate.swift; sourceTree = "<group>"; }; D2169300251E51E7002A6324 /* SectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplate.swift; sourceTree = "<group>"; };
@ -1295,6 +1297,8 @@
0AE98BAD23FEF92B004C5109 /* Link */ = { 0AE98BAD23FEF92B004C5109 /* Link */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1D6D258726899B0B00DEBB08 /* ImageButton.swift */,
1D6D258626899B0B00DEBB08 /* ImageButtonModel.swift */,
D28A838823CCCFCB00DFE4FC /* LinkModel.swift */, D28A838823CCCFCB00DFE4FC /* LinkModel.swift */,
C07065C32395677300FBF997 /* Link.swift */, C07065C32395677300FBF997 /* Link.swift */,
0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */, 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */,
@ -2324,7 +2328,6 @@
D20F3B43252E00E4004B3F56 /* PageProtocol.swift */, D20F3B43252E00E4004B3F56 /* PageProtocol.swift */,
012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */,
D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */, D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */,
D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */,
D2B9D0E3265EEE9D0084735C /* MoleculeListProtocol.swift */, D2B9D0E3265EEE9D0084735C /* MoleculeListProtocol.swift */,
011B58EE23A2AA850085F53C /* ModelProtocols */, 011B58EE23A2AA850085F53C /* ModelProtocols */,
); );
@ -2760,7 +2763,6 @@
D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */,
D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */,
0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */, 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */,
D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */,
D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */, D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */,
D260105D23D0BCD400764D80 /* Stack.swift in Sources */, D260105D23D0BCD400764D80 /* Stack.swift in Sources */,
0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */,
@ -2894,6 +2896,7 @@
D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */, D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */,
AA617AB02453010A00910B8F /* ListDeviceComplexLinkSmall.swift in Sources */, AA617AB02453010A00910B8F /* ListDeviceComplexLinkSmall.swift in Sources */,
D23A8FD9260CE004007E14CE /* MFStyler+PaddingExtension.swift in Sources */, D23A8FD9260CE004007E14CE /* MFStyler+PaddingExtension.swift in Sources */,
1D6D258926899B0C00DEBB08 /* ImageButton.swift in Sources */,
C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */, C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */,
01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */, 01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */,
D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */,
@ -2939,6 +2942,7 @@
BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */, BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */,
943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */, 943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */,
011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */, 011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */,
1D6D258826899B0C00DEBB08 /* ImageButtonModel.swift in Sources */,
AA07EA912510A442009A2AE3 /* StarModel.swift in Sources */, AA07EA912510A442009A2AE3 /* StarModel.swift in Sources */,
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */, D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,
011D95892404249B000E3791 /* FormHolderModelProtocol.swift in Sources */, 011D95892404249B000E3791 /* FormHolderModelProtocol.swift in Sources */,

View File

@ -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
}
}
}

View File

@ -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)
}
}

View File

@ -24,7 +24,6 @@ import Foundation
super.setupView() super.setupView()
layer.borderColor = UIColor.mvmCoolGray6.cgColor layer.borderColor = UIColor.mvmCoolGray6.cgColor
layer.borderWidth = 1 layer.borderWidth = 1
label.numberOfLines = 1
addSubview(label) addSubview(label)
NSLayoutConstraint.constraintPinSubview(label, pinTop: true, topConstant: 13, pinBottom: true, bottomConstant: 13, pinLeft: true, leftConstant: 15, pinRight: true, rightConstant: 15) NSLayoutConstraint.constraintPinSubview(label, pinTop: true, topConstant: 13, pinBottom: true, bottomConstant: 13, pinLeft: true, leftConstant: 15, pinRight: true, rightConstant: 15)
} }

View File

@ -21,6 +21,12 @@ import Foundation
case backgroundColor case backgroundColor
} }
public func setDefaults() {
if label.numberOfLines == nil {
label.numberOfLines = 1
}
}
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
label = try typeContainer.decode(LabelModel.self, forKey: .label) label = try typeContainer.decode(LabelModel.self, forKey: .label)

View File

@ -124,7 +124,7 @@ import MVMCore
didSet { didSet {
if !updateSelectionOnly { if !updateSelectionOnly {
layoutIfNeeded() layoutIfNeeded()
(model as? CheckboxModel)?.checked = isSelected (model as? CheckboxModel)?.selected = isSelected
shapeLayer?.removeAllAnimations() shapeLayer?.removeAllAnimations()
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
@ -419,8 +419,8 @@ import MVMCore
isAnimated = model.animated isAnimated = model.animated
isRound = model.round isRound = model.round
if model.checked { if model.selected {
checkAndBypassAnimations(selected: model.checked) checkAndBypassAnimations(selected: model.selected)
} }
isEnabled = model.enabled isEnabled = model.enabled

View File

@ -6,10 +6,14 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // 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, SelectableMoleculeModelProtocol, FormFieldProtocol {
@objcMembers public class CheckboxModel: MoleculeModelProtocol, FormFieldProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -17,7 +21,7 @@ import Foundation
public static var identifier: String = "checkbox" public static var identifier: String = "checkbox"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String? public var accessibilityIdentifier: String?
public var checked: Bool = false public var selected: Bool = false
public var enabled: Bool = true public var enabled: Bool = true
public var animated: Bool = true public var animated: Bool = true
public var inverted: Bool = false public var inverted: Bool = false
@ -68,17 +72,17 @@ import Foundation
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Form Validation
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { checked } public func formFieldValue() -> AnyHashable? { selected }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public init(isChecked: Bool = false) { public init(isChecked: Bool = false) {
self.checked = isChecked self.selected = isChecked
baseValue = isChecked baseValue = isChecked
} }
@ -132,10 +136,10 @@ import Foundation
} }
if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) { 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) { if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) {
self.animated = animated self.animated = animated
@ -169,7 +173,7 @@ import Foundation
try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(borderColor, forKey: .borderColor) try container.encodeIfPresent(borderColor, forKey: .borderColor)
try container.encode(borderWidth, forKey: .borderWidth) 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.encode(inverted, forKey: .inverted)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(checkColor, forKey: .checkColor) try container.encodeIfPresent(checkColor, forKey: .checkColor)

View File

@ -8,6 +8,10 @@
import Foundation import Foundation
public protocol RadioBoxSelectionDelegate: class {
func selected(radioBox: RadioBoxModel)
}
open class RadioBoxes: View { open class RadioBoxes: View {
public var collectionView: CollectionView! public var collectionView: CollectionView!
@ -19,7 +23,7 @@ open class RadioBoxes: View {
private var radioBoxesModel: RadioBoxesModel? { private var radioBoxesModel: RadioBoxesModel? {
return model as? RadioBoxesModel return model as? RadioBoxesModel
} }
public weak var radioDelegate: RadioBoxSelectionDelegate?
private var delegateObject: MVMCoreUIDelegateObject? private var delegateObject: MVMCoreUIDelegateObject?
/// The models for the molecules. /// The models for the molecules.
@ -37,7 +41,7 @@ open class RadioBoxes: View {
open func updateAccessibilityValue(collectionView: UICollectionView, cell: RadioBoxCollectionViewCell, indexPath: IndexPath) { open func updateAccessibilityValue(collectionView: UICollectionView, cell: RadioBoxCollectionViewCell, indexPath: IndexPath) {
guard let format = MVMCoreUIUtility.hardcodedString(withKey: "index_string_of_total"), 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) let total = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section)
cell.accessibilityValue = String(format: format, indexString, total) cell.accessibilityValue = String(format: format, indexString, total)
} }
@ -127,7 +131,7 @@ extension RadioBoxes: UICollectionViewDataSource {
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let molecule = boxes?[indexPath.row], 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() fatalError()
} }
cell.reset() cell.reset()
@ -162,6 +166,8 @@ extension RadioBoxes: UICollectionViewDelegate {
cell.radioBox.selectBox() cell.radioBox.selectBox()
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
cell.updateAccessibility() cell.updateAccessibility()
guard let radioBox = boxes?[indexPath.row] else { return }
radioDelegate?.selected(radioBox: radioBox)
} }
open func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { open func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {

View File

@ -98,7 +98,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
self.constrainKnob() self.constrainKnob()
} }
toggleModel?.state = isOn toggleModel?.selected = isOn
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff")
setNeedsLayout() setNeedsLayout()
@ -381,7 +381,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
containerTintColor.off = model.offTintColor.uiColor containerTintColor.off = model.offTintColor.uiColor
knobTintColor.on = model.onKnobTintColor.uiColor knobTintColor.on = model.onKnobTintColor.uiColor
knobTintColor.off = model.offKnobTintColor.uiColor knobTintColor.off = model.offKnobTintColor.uiColor
isOn = model.state isOn = model.selected
changeStateNoAnimation(isOn) changeStateNoAnimation(isOn)
isAnimated = model.animated isAnimated = model.animated
isEnabled = model.enabled isEnabled = model.enabled

View File

@ -15,7 +15,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
public static var identifier: String = "toggle" public static var identifier: String = "toggle"
public var accessibilityIdentifier: String? public var accessibilityIdentifier: String?
public var backgroundColor: Color? public var backgroundColor: Color?
public var state: Bool = false public var selected: Bool = false
public var animated: Bool = true public var animated: Bool = true
public var enabled: Bool = true public var enabled: Bool = true
public var action: ActionModelProtocol? 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 // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public init(_ state: Bool) { public init(_ state: Bool) {
self.state = state self.selected = state
baseValue = state baseValue = state
} }
@ -75,7 +75,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { 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) { 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) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
baseValue = state baseValue = selected
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName self.groupName = groupName
@ -123,7 +123,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(action, forKey: .action)
try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction)
try container.encode(moleculeName, forKey: .moleculeName) 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(animated, forKey: .animated)
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)
try container.encode(onTintColor, forKey: .onTintColor) try container.encode(onTintColor, forKey: .onTintColor)

View File

@ -316,6 +316,10 @@ public typealias ActionBlock = () -> ()
textColor = color.uiColor textColor = color.uiColor
} }
if let lines = labelModel.numberOfLines {
numberOfLines = lines
}
if let attributes = labelModel.attributes, let labelText = text { 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]) 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 = [] accessibilityCustomActions = []
clauses = [] clauses = []
accessibilityTraits = .staticText accessibilityTraits = .staticText
numberOfLines = 0
} }
public func needsToBeConstrained() -> Bool { true } public func needsToBeConstrained() -> Bool { true }

View File

@ -25,6 +25,7 @@
public var html: String? public var html: String?
public var hero: Int? public var hero: Int?
public var makeWholeViewClickable: Bool? public var makeWholeViewClickable: Bool?
public var numberOfLines: Int?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -44,6 +45,7 @@
case html case html
case hero case hero
case makeWholeViewClickable case makeWholeViewClickable
case numberOfLines
} }
enum AttributeTypeKey: String, CodingKey { enum AttributeTypeKey: String, CodingKey {
@ -76,9 +78,10 @@
html = try typeContainer.decodeIfPresent(String.self, forKey: .html) html = try typeContainer.decodeIfPresent(String.self, forKey: .html)
hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero) hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero)
makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable) 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) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
try container.encode(text, forKey: .text) try container.encode(text, forKey: .text)
@ -93,5 +96,6 @@
try container.encodeIfPresent(html, forKey: .html) try container.encodeIfPresent(html, forKey: .html)
try container.encodeIfPresent(hero, forKey: .hero) try container.encodeIfPresent(hero, forKey: .hero)
try container.encodeIfPresent(makeWholeViewClickable, forKey: .makeWholeViewClickable) try container.encodeIfPresent(makeWholeViewClickable, forKey: .makeWholeViewClickable)
try container.encodeIfPresent(numberOfLines, forKey: .numberOfLines)
} }
} }

View File

@ -0,0 +1,9 @@
//
// UITableViewRowAnimation.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 7/26/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation

View File

@ -48,11 +48,8 @@ import Foundation
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
setDefaults() setDefaults()
body.numberOfLines = 1
body.lineBreakMode = .byTruncatingTail body.lineBreakMode = .byTruncatingTail
body2.numberOfLines = 1
body2.lineBreakMode = .byTruncatingTail body2.lineBreakMode = .byTruncatingTail
body3.numberOfLines = 1
body3.lineBreakMode = .byTruncatingTail body3.lineBreakMode = .byTruncatingTail
verticalLine1.widthConstraint?.isActive = true verticalLine1.widthConstraint?.isActive = true

View File

@ -49,6 +49,15 @@ public class HeadersH2PricingTwoRowsModel: HeaderModel, MoleculeModelProtocol {
subBody?.attributes = [LabelAttributeStrikeThroughModel(0, subBody?.text.count ?? 0)] subBody?.attributes = [LabelAttributeStrikeThroughModel(0, subBody?.text.count ?? 0)]
subBody2?.attributes = [LabelAttributeStrikeThroughModel(0, subBody2?.text.count ?? 0)] subBody2?.attributes = [LabelAttributeStrikeThroughModel(0, subBody2?.text.count ?? 0)]
subBody3?.attributes = [LabelAttributeStrikeThroughModel(0, subBody3?.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
}
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -50,7 +50,6 @@
leftImage.contentMode = .scaleAspectFit leftImage.contentMode = .scaleAspectFit
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
} }

View File

@ -31,6 +31,10 @@ public class ListLeftVariableIconWithRightCaretAllTextLinksModel: ListItemModel,
image.width = 30 image.width = 30
image.height = 30 image.height = 30
} }
if rightLabel.numberOfLines == nil {
rightLabel.numberOfLines = 1
}
} }
//----------------------------------------------------- //-----------------------------------------------------

View File

@ -55,7 +55,6 @@
leftImage.contentMode = .scaleAspectFit leftImage.contentMode = .scaleAspectFit
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
} }

View File

@ -7,7 +7,7 @@
// //
public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, MoleculeModelProtocol { public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, ParentMoleculeModelProtocol {
//----------------------------------------------------- //-----------------------------------------------------
// MARK: - Properties // MARK: - Properties
//----------------------------------------------------- //-----------------------------------------------------
@ -17,6 +17,10 @@ public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, Mol
public var headlineBody: HeadlineBodyModel public var headlineBody: HeadlineBodyModel
public var rightLabel: LabelModel public var rightLabel: LabelModel
public var children: [MoleculeModelProtocol] {
[image, headlineBody, rightLabel]
}
//----------------------------------------------------- //-----------------------------------------------------
// MARK: - Methods // MARK: - Methods
//----------------------------------------------------- //-----------------------------------------------------
@ -29,6 +33,9 @@ public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, Mol
} }
headlineBody.style = .item headlineBody.style = .item
headlineBody.headline?.hero = 0 headlineBody.headline?.hero = 0
if rightLabel.numberOfLines == nil {
rightLabel.numberOfLines = 1
}
} }
//----------------------------------------------------- //-----------------------------------------------------

View File

@ -71,7 +71,6 @@
rightBar.widthAnchor.constraint(equalToConstant: 20).isActive = true rightBar.widthAnchor.constraint(equalToConstant: 20).isActive = true
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
horizontalStack.restack() horizontalStack.restack()

View File

@ -45,6 +45,10 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol {
rightBar.backgroundColor = Color(uiColor: .gray) rightBar.backgroundColor = Color(uiColor: .gray)
} }
if rightLabel.numberOfLines == nil {
rightLabel.numberOfLines = 1
}
leftHeadline.hero = 0 leftHeadline.hero = 0
} }

View File

@ -39,7 +39,6 @@
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
horizontalStack.restack() horizontalStack.restack()

View File

@ -38,6 +38,9 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
override public func setDefaults() { override public func setDefaults() {
super.setDefaults() super.setDefaults()
leftHeadline.hero = 0 leftHeadline.hero = 0
if rightLabel.numberOfLines == nil {
rightLabel.numberOfLines = 1
}
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -61,7 +61,6 @@
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
} }

View File

@ -17,6 +17,17 @@ public class ListRightVariablePriceChangeAllTextAndLinksModel: ListItemModel, Mo
public var rightLabel: LabelModel public var rightLabel: LabelModel
public var arrow: ArrowModel public var arrow: ArrowModel
//-----------------------------------------------------
// MARK: - Methods
//-----------------------------------------------------
override public func setDefaults() {
super.setDefaults()
if rightLabel.numberOfLines == nil {
rightLabel.numberOfLines = 1
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------

View File

@ -46,7 +46,6 @@
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()

View File

@ -37,6 +37,9 @@ public class ListRightVariablePriceChangeBodyTextModel: ListItemModel, MoleculeM
if let headline = headlineBody.headline { if let headline = headlineBody.headline {
headline.hero = 0 headline.hero = 0
} }
if rightLabel.numberOfLines == nil {
rightLabel.numberOfLines = 1
}
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -38,7 +38,6 @@
super.setupView() super.setupView()
rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
} }

View File

@ -7,7 +7,7 @@
// //
public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, ParentMoleculeModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -16,6 +16,21 @@ public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, Mol
public var rightLabel: LabelModel public var rightLabel: LabelModel
public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel 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 // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------

View File

@ -39,9 +39,6 @@
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
leftLabel.numberOfLines = 1
centerLabel.numberOfLines = 1
rightLabel.numberOfLines = 1
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
} }

View File

@ -17,6 +17,23 @@ public class ListThreeColumnBillChangesModel: ListItemModel, MoleculeModelProtoc
public var centerLabel: LabelModel public var centerLabel: LabelModel
public var rightLabel: 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 // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------

View File

@ -74,9 +74,6 @@
view.addSubview(rightVerticalStack) view.addSubview(rightVerticalStack)
NSLayoutConstraint.pinViews(leftView: leftVerticalStack, rightView: rightVerticalStack, alignTop: true) NSLayoutConstraint.pinViews(leftView: leftVerticalStack, rightView: rightVerticalStack, alignTop: true)
leftHeadline.numberOfLines = 1
rightLabel.numberOfLines = 1
rightSubLabel.numberOfLines = 1
} }
//---------------------------------------------------- //----------------------------------------------------

View File

@ -34,6 +34,18 @@ public class ListTwoColumnPriceDescriptionModel: ListItemModel, MoleculeModelPro
if rightSubLabel.attributes == nil { if rightSubLabel.attributes == nil {
rightSubLabel.attributes = [LabelAttributeStrikeThroughModel(0, rightSubLabel.text.count)] 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
}
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -64,6 +64,7 @@ open class DoughnutChart: View {
open override func reset() { open override func reset() {
super.reset() super.reset()
titleLabel.reset() titleLabel.reset()
titleLabel.numberOfLines = 1
subTitleLabel.reset() subTitleLabel.reset()
clearLayers() clearLayers()
} }

View File

@ -13,7 +13,8 @@ import UIKit
func didSelectItem(_ indexPath: IndexPath, tabs: Tabs) func didSelectItem(_ indexPath: IndexPath, tabs: Tabs)
} }
@objcMembers open class Tabs: View, MVMCoreUIViewConstrainingProtocol { @objcMembers open class Tabs: View, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {
public var tabsModel: TabsModel? { public var tabsModel: TabsModel? {
get { return model as? TabsModel } get { return model as? TabsModel }

View File

@ -8,7 +8,7 @@
import UIKit import UIKit
public class CornerLabelsModel: MoleculeModelProtocol { public class CornerLabelsModel: ParentMoleculeModelProtocol {
public static var identifier: String = "cornerLabels" public static var identifier: String = "cornerLabels"
public var backgroundColor: Color? public var backgroundColor: Color?
public var topLeftLabel: LabelModel? public var topLeftLabel: LabelModel?
@ -16,6 +16,9 @@ public class CornerLabelsModel: MoleculeModelProtocol {
public var bottomLeftLabel: LabelModel? public var bottomLeftLabel: LabelModel?
public var bottomRightLabel: LabelModel? public var bottomRightLabel: LabelModel?
public var molecule: MoleculeModelProtocol? public var molecule: MoleculeModelProtocol?
public var children: [MoleculeModelProtocol] {
[molecule, topLeftLabel, topRightLabel, bottomLeftLabel, bottomRightLabel].compactMap { $0 }
}
init(with molecule: MoleculeModelProtocol?) { init(with molecule: MoleculeModelProtocol?) {
self.molecule = molecule self.molecule = molecule
@ -35,20 +38,20 @@ public class CornerLabelsModel: MoleculeModelProtocol {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
molecule = try typeContainer.decodeModelIfPresent(codingKey: .molecule) molecule = try typeContainer.decodeModelIfPresent(codingKey: .molecule)
topLeftLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .topLeftLabel) topLeftLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .topLeftLabel)
topRightLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .topRightLabel) topRightLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .topRightLabel)
bottomLeftLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .bottomLeftLabel) bottomLeftLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .bottomLeftLabel)
bottomRightLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .bottomRightLabel) bottomRightLabel = try typeContainer.decodeMoleculeIfPresent(codingKey: .bottomRightLabel)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeModelIfPresent(molecule, forKey: .molecule) try container.encodeModelIfPresent(molecule, forKey: .molecule)
try container.encodeIfPresent(topLeftLabel, forKey: .topLeftLabel) try container.encodeModelIfPresent(topLeftLabel, forKey: .topLeftLabel)
try container.encodeIfPresent(topRightLabel, forKey: .topRightLabel) try container.encodeModelIfPresent(topRightLabel, forKey: .topRightLabel)
try container.encodeIfPresent(bottomLeftLabel, forKey: .bottomLeftLabel) try container.encodeModelIfPresent(bottomLeftLabel, forKey: .bottomLeftLabel)
try container.encodeIfPresent(bottomRightLabel, forKey: .bottomRightLabel) try container.encodeModelIfPresent(bottomRightLabel, forKey: .bottomRightLabel)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
} }
} }

View File

@ -20,16 +20,9 @@
} }
/// Creates the item with the passed in action. /// 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) let button = create(with: image)
button.set(with: actionModel, delegateObject: delegateObject, additionalData: additionalData) button.set(with: model, 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)
return button return button
} }

View File

@ -21,16 +21,9 @@
} }
/// Creates the item with the passed in action. /// 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) let button = create(with: title)
button.set(with: actionModel, delegateObject: delegateObject, additionalData: additionalData) button.set(with: model, 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)
return button return button
} }

View File

@ -67,7 +67,7 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
/// Convenience function that creates a BarButtonItem for the model. /// Convenience function that creates a BarButtonItem for the model.
public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem {
let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image) 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 buttonItem.accessibilityIdentifier = accessibilityIdentifier ?? image
if let accessibilityString = accessibilityText { if let accessibilityString = accessibilityText {
buttonItem.accessibilityLabel = accessibilityString buttonItem.accessibilityLabel = accessibilityString

View File

@ -7,16 +7,16 @@
// //
public class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol { open class NavigationLabelButtonModel: NavigationButtonModelProtocol, MoleculeModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public var backgroundColor: Color? open var backgroundColor: Color?
public static var identifier: String = "navigationLabelButton" open class var identifier: String { "navigationLabelButton" }
public var accessibilityIdentifier: String? open var accessibilityIdentifier: String?
public var title: String open var title: String
public var action: ActionModelProtocol open var action: ActionModelProtocol
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer
@ -62,7 +62,7 @@ public class NavigationLabelButtonModel: NavigationButtonModelProtocol, Molecule
//-------------------------------------------------- //--------------------------------------------------
/// Convenience function that creates a BarButtonItem for the model. /// Convenience function that creates a BarButtonItem for the model.
public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { open func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem {
return LabelBarButtonItem.create(with: title, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) return LabelBarButtonItem.create(with: title, model: self, delegateObject: delegateObject, additionalData: additionalData)
} }
} }

View File

@ -91,3 +91,16 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc
try container.encodeModelIfPresent(titleView, forKey: .titleView) 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
}
}

View File

@ -26,6 +26,9 @@ open class CollapsableNotificationModel: NotificationModel {
open override func setDefaults() { open override func setDefaults() {
super.setDefaults() super.setDefaults()
if topLabel.numberOfLines == nil {
topLabel.numberOfLines = 1
}
if topLabel.textColor == nil { if topLabel.textColor == nil {
topLabel.textColor = Color(uiColor: .white) topLabel.textColor = Color(uiColor: .white)
} }

View File

@ -11,7 +11,6 @@ import Foundation
@objcMembers open class CollapsableNotificationTopView: View { @objcMembers open class CollapsableNotificationTopView: View {
public let label: Label = { public let label: Label = {
let label = Label(fontStyle: .BoldBodySmall) let label = Label(fontStyle: .BoldBodySmall)
label.numberOfLines = 1
label.textAlignment = .center label.textAlignment = .center
label.setContentHuggingPriority(.defaultHigh, for: .vertical) label.setContentHuggingPriority(.defaultHigh, for: .vertical)
return label return label

View File

@ -6,13 +6,13 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import Foundation
public protocol MoleculeDelegateProtocol: AnyObject { public protocol MoleculeDelegateProtocol: AnyObject {
func getRootMolecules() -> [MoleculeModelProtocol] func getRootMolecules() -> [MoleculeModelProtocol]
/// returns a module for the corresponding module name. /// 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? func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol?
} }

View File

@ -28,7 +28,7 @@ public protocol MoleculeTreeTraversalProtocol {
// Helper Extensions // Helper Extensions
// //
extension MoleculeTreeTraversalProtocol { public extension MoleculeTreeTraversalProtocol {
func countMolecules(options: TreeTraversalOptions = .parentFirst) -> Int { func countMolecules(options: TreeTraversalOptions = .parentFirst) -> Int {
return reduceDepthFirstTraverse(options: options, depth: 0, initialResult: 0) { (accumulator, molecule, depth) in return reduceDepthFirstTraverse(options: options, depth: 0, initialResult: 0) { (accumulator, molecule, depth) in
@ -50,5 +50,4 @@ extension MoleculeTreeTraversalProtocol {
return accumulator return accumulator
} }
} }
} }

View File

@ -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)
}
}

View File

@ -66,7 +66,7 @@
return molecule return molecule
} }
open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool { open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>) -> Bool {
guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false } guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false }
// This template requires atleast one of the three layers. // This template requires atleast one of the three layers.

View File

@ -178,6 +178,98 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
tableView.endUpdates() 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 // MARK: - Convenience
//-------------------------------------------------- //--------------------------------------------------
@ -245,57 +337,91 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
extension MoleculeListTemplate: MoleculeListProtocol { extension MoleculeListTemplate: MoleculeListProtocol {
public func removeMolecules(at indexPaths: [IndexPath], animation: UITableView.RowAnimation?) { open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {
for (index, indexPath) in indexPaths.sorted().enumerated() { guard let tableView = tableView else { return }
let removeIndex = indexPath.row - index if let indexPath = tableView.indexPathForRow(at: molecule.center), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false {
moleculesInfo?.remove(at: removeIndex) performTableViewUpdates()
} }
}
guard let animation = animation,
indexPaths.count > 0 else { return } open override func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? {
tableView?.deleteRows(at: indexPaths, with: animation) guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
updateViewConstraints() //TODO: check for molecule protocol eqaulity
view.layoutIfNeeded() let json = moleculeInfo.molecule.toJSON()
} return json == molecule.toJSON()
}) else { return nil }
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. return IndexPath(row: index, section: 0)
DispatchQueue.main.async { }
var indexPaths: [IndexPath] = []
open override func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) {
for molecule in molecules { // This dispatch is needed to fix a race condition that can occur if this function is called during the table setup.
if let info = self.createMoleculeInfo(with: molecule) { DispatchQueue.main.async {
self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) var indexPaths: [IndexPath] = []
let index = indexPath.row + indexPaths.count
self.moleculesInfo?.insert(info, at: index) for molecule in molecules {
indexPaths.append(IndexPath(row: index, section: 0)) 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)
guard let animation = animation, indexPaths.append(IndexPath(row: index, section: 0))
indexPaths.count > 0 else { return } }
self.tableView?.insertRows(at: indexPaths, with: animation) }
self.updateViewConstraints()
self.view.layoutIfNeeded() guard 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() open func newData(for molecule: MoleculeModelProtocol) {
return json == molecule.toJSON() //TODO: expand for header, navigation, etc
}) else { return nil } let json = molecule.toJSON()
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
return IndexPath(row: index, section: 0) //TODO: check for molecule protocol eqaulity
} if json == moleculeInfo.molecule.toJSON() {
return true
open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { } else if let parent = moleculeInfo.molecule as? ParentMoleculeModelProtocol {
guard let tableView = tableView else { return } // Get all molecules of the same type for faster check.
let molecules: [MoleculeModelProtocol] = parent.reduceDepthFirstTraverse(options: .childFirst, depth: 0, initialResult: []) { (accumulator, currentMolecule, depth) in
let point = molecule.convert(molecule.bounds.origin, to: tableView) if currentMolecule.moleculeName == molecule.moleculeName {
if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { return accumulator + [currentMolecule]
performTableViewUpdates() }
} 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()
}
} }

View File

@ -16,7 +16,7 @@
public var moleculeStack: StackModel public var moleculeStack: StackModel
public override var rootMolecules: [MoleculeModelProtocol] { public override var rootMolecules: [MoleculeModelProtocol] {
return [header, moleculeStack, footer].compactMap { $0 } [navigationBar, header, moleculeStack, footer].compactMap { $0 }
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -13,8 +13,7 @@ import Foundation
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
open class var identifier: String { "" }
public class var identifier: String { "" }
public var pageType: String public var pageType: String
public var template: String { public var template: String {
@ -74,7 +73,7 @@ import Foundation
tabBarIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .tabBarIndex) 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) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(pageType, forKey: .pageType) try container.encode(pageType, forKey: .pageType)
try container.encode(template, forKey: .template) try container.encode(template, forKey: .template)

View File

@ -18,7 +18,7 @@
public var footer: MoleculeModelProtocol? public var footer: MoleculeModelProtocol?
public override var rootMolecules: [MoleculeModelProtocol] { public override var rootMolecules: [MoleculeModelProtocol] {
return [header, footer].compactMap { $0 } [navigationBar, header, footer].compactMap { $0 }
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -20,7 +20,7 @@ public typealias BarButtonAction = (BarButtonItem) -> ()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Delegate // MARK: - Delegate
//-------------------------------------------------- //--------------------------------------------------
open var model: (MoleculeModelProtocol & NavigationButtonModelProtocol)?
open weak var buttonDelegate: ButtonDelegateProtocol? open weak var buttonDelegate: ButtonDelegateProtocol?
var actionDelegate: ActionDelegate? var actionDelegate: ActionDelegate?
@ -28,18 +28,12 @@ public typealias BarButtonAction = (BarButtonItem) -> ()
// MARK: - Methods // 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 buttonDelegate = delegateObject?.buttonDelegate
actionDelegate?.buttonAction = { sender in actionDelegate?.buttonAction = { sender in
Button.performButtonAction(with: actionModel, button: sender, delegateObject: delegateObject, additionalData: additionalData) let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model)
} Button.performButtonAction(with: model.action, button: sender, delegateObject: delegateObject, additionalData: additionalDataWithSource)
}
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)
} }
} }
} }

View File

@ -114,17 +114,6 @@ import UIKit
try parsePageJSON() try parsePageJSON()
MVMCoreDispatchUtility.performBlock(onMainThread: { MVMCoreDispatchUtility.performBlock(onMainThread: {
self.handleNewDataAndUpdateUI() 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 { } catch {
if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") { 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<MVMCoreErrorObject>) -> Bool { open func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>) -> Bool {
pageType = loadObject.pageType pageType = loadObject.pageType
self.loadObject = loadObject self.loadObject = loadObject
@ -197,7 +186,7 @@ import UIKit
open func parsePageJSON() throws { } open func parsePageJSON() throws { }
open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool { open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>) -> Bool {
guard let pageType = loadObject?.pageType, guard let pageType = loadObject?.pageType,
var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType),
!modulesRequired.isEmpty !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, /// Processes any new data. Called after the page is loaded the first time and on response updates for this page,
open func handleNewData() { open func handleNewData() {
if model?.navigationBar == nil {
let navigationItem = createDefaultLegacyNavigationModel()
model?.navigationBar = navigationItem
}
executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in
behavior.onPageNew(rootMolecules: getRootMolecules(), delegateObjectIVar) behavior.onPageNew(rootMolecules: getRootMolecules(), delegateObjectIVar)
} }
@ -250,8 +244,14 @@ import UIKit
view.backgroundColor = backgroundColor.uiColor view.backgroundColor = backgroundColor.uiColor
} }
// Sets up the navigation item based on the data. // Update splitview properties
setNavigationItem() 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 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 // MARK: - TabBar
//-------------------------------------------------- //--------------------------------------------------
@ -378,9 +356,6 @@ import UIKit
updateTabBar() updateTabBar()
} }
// Update the navigation bar ui when view is appearing.
setNavigationBar()
// Track. // Track.
MVMCoreUISession.sharedGlobal()?.currentPageType = pageType MVMCoreUISession.sharedGlobal()?.currentPageType = pageType
MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self)
@ -475,10 +450,12 @@ import UIKit
addFormParams(requestParameters: requestParameters, actionInformation: actionInformation, additionalData: additionalData) addFormParams(requestParameters: requestParameters, actionInformation: actionInformation, additionalData: additionalData)
requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType")
var pageForwardedData = additionalData ?? [:] var pageForwardedData = additionalData ?? [:]
executeBehaviors { (behavior: PageLocalDataShareBehavior) in executeBehaviors { (behavior: PageLocalDataShareBehavior) in
let dataMap = behavior.compileLocalPageDataForTransfer(delegateObjectIVar) 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()) 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) 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 var handled = false
executeBehaviors { (behavior: PageCustomActionHandlerBehavior) in executeBehaviors { (behavior: PageCustomActionHandlerBehavior) in
if (!handled) { if !handled {
handled = behavior.handleAction(type: actionType, information: actionInformation, additionalData: additionalData) handled = behavior.handleAction(type: actionType, information: actionInformation, additionalData: additionalData)
} }
} }
if (!handled) {
if !handled {
MVMCoreUIActionHandler.defaultHandleUnknownActionType(actionType, actionInformation: actionInformation, additionalData: additionalData, delegateObject: delegateObjectIVar) MVMCoreUIActionHandler.defaultHandleUnknownActionType(actionType, actionInformation: actionInformation, additionalData: additionalData, delegateObject: delegateObjectIVar)
} }
} }
@ -503,7 +482,7 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
open func getRootMolecules() -> [MoleculeModelProtocol] { open func getRootMolecules() -> [MoleculeModelProtocol] {
return model?.rootMolecules ?? [] model?.rootMolecules ?? []
} }
open func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? { open func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? {
@ -530,11 +509,6 @@ import UIKit
// MARK: - MVMCoreUIDetailViewProtocol // MARK: - MVMCoreUIDetailViewProtocol
//-------------------------------------------------- //--------------------------------------------------
// Reset the navigation state.
public func splitViewDidReset() {
setNavigationBar()
}
public func isLeftPanelAccessible() -> Bool { public func isLeftPanelAccessible() -> Bool {
// TODO: Remove when hamburger menu is fully phased out. // TODO: Remove when hamburger menu is fully phased out.
if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false {

View File

@ -9,6 +9,7 @@
import Foundation import Foundation
import Contacts import Contacts
public protocol PageGetContactBehaviorConsumerProtocol { public protocol PageGetContactBehaviorConsumerProtocol {
func getMatchParameters() -> (NSPredicate, [CNKeyDescriptor])? func getMatchParameters() -> (NSPredicate, [CNKeyDescriptor])?
func consume(contacts: [CNContact]) func consume(contacts: [CNContact])
@ -18,7 +19,7 @@ public class PageGetContactBehaviorModel: PageBehaviorModelProtocol {
public class var identifier: String { "pageGetContactBehavior" } public class var identifier: String { "pageGetContactBehavior" }
public var shouldAllowMultipleInstances: Bool { false } public var shouldAllowMultipleInstances: Bool { false }
public init() {} public init() { }
} }
public class PageGetContactBehavior: PageVisibilityBehavior { public class PageGetContactBehavior: PageVisibilityBehavior {
@ -41,7 +42,8 @@ public class PageGetContactBehavior: PageVisibilityBehavior {
// Tell template to update // Tell template to update
MVMCoreDispatchUtility.performBlock(onMainThread: { MVMCoreDispatchUtility.performBlock(onMainThread: {
// TODO: move to protocol function instead // TODO: move to protocol function instead
(self?.delegate?.moleculeDelegate as? ViewController)?.handleNewDataAndUpdateUI() guard let controller = self?.delegate?.moleculeDelegate as? ViewController else { return }
controller.handleNewDataAndUpdateUI()
}) })
} }
} }

View File

@ -41,6 +41,11 @@ public protocol PageLocalDataShareBehavior: PageBehaviorProtocol {
public protocol PageCustomActionHandlerBehavior: 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 func handleAction(type actionType: String?, information: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> Bool
} }

View File

@ -8,8 +8,9 @@
import UIKit import UIKit
@objcMembers open class NavigationController: UINavigationController { @objcMembers open class NavigationController: UINavigationController, MVMCoreViewManagerViewControllerProtocol {
public var separatorView: Line? public var separatorView: Line?
public var manager: (UIViewController & MVMCoreViewManagerProtocol)?
/// Getter for the main navigation controller /// Getter for the main navigation controller
public static func navigationController() -> Self? { public static func navigationController() -> Self? {
@ -33,6 +34,7 @@ import UIKit
MVMCoreUISession.sharedGlobal()?.navigationController = navigationController MVMCoreUISession.sharedGlobal()?.navigationController = navigationController
MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController
MVMCoreNavigationHandler.shared()?.navigationController = navigationController MVMCoreNavigationHandler.shared()?.navigationController = navigationController
MVMCoreNavigationHandler.shared()?.addDelegate(navigationController)
return navigationController return navigationController
} }
@ -103,4 +105,83 @@ import UIKit
viewController.navigationItem.titleView = molecule 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)
}
}
} }

View File

@ -48,9 +48,6 @@ NS_ASSUME_NONNULL_BEGIN
- (UIStatusBarStyle)defaultStatusBarStyle; - (UIStatusBarStyle)defaultStatusBarStyle;
- (nullable UIColor *)defaultStatusBarBackgroundColor; - (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 @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -8,15 +8,15 @@
import Foundation import Foundation
// Navigation bar update functions
public extension MVMCoreUISplitViewController { 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. /// 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) { static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) {
guard let splitView = MVMCoreUISplitViewController.main(), guard let splitView = MVMCoreUISplitViewController.main(),
navigationController == splitView.navigationController, navigationController == splitView.navigationController,
navigationController.topViewController == viewController else { viewController == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() else {
/// Not the split view navigation controller, skip split functions. /// Not the split view navigation controller, skip split functions.
NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController)
return return
} }
splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel) 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 /// 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) { 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) setLeftPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false)
setRightPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?() ?? 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. /// Sets the left navigation items for the view controller based on model and splitview.
func setLeftNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { func setLeftNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) {
guard let topViewController = navigationController.topViewController else { return }
var leftItems: [UIBarButtonItem] = [] var leftItems: [UIBarButtonItem] = []
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
@ -75,11 +75,13 @@ public extension MVMCoreUISplitViewController {
leftItems.append(contentsOf: additionalLeftButtons) 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. /// Sets the right navigation items for the view controller based on model and splitview.
func setRightNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { func setRightNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) {
guard let topViewController = navigationController.topViewController else { return }
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
var rightItems: [UIBarButtonItem] = [] var rightItems: [UIBarButtonItem] = []
@ -102,6 +104,38 @@ public extension MVMCoreUISplitViewController {
rightItems.append(contentsOf: additionalRightButtons) 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)
} }
} }

View File

@ -126,6 +126,9 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) {
- (UIStatusBarStyle)getDefaultStatusBarStyle; - (UIStatusBarStyle)getDefaultStatusBarStyle;
- (nullable UIColor *)getDefaultStatusBarBackgroundColor; - (nullable UIColor *)getDefaultStatusBarBackgroundColor;
/// Returns true if a panel is showing.
- (BOOL)isAPanelShowing;
#pragma mark - Main Subclassables #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. // Can subclass to set threshold for when the drawers are permanently extended. Default is 1000 for the left panel and 2000 for both.

View File

@ -240,7 +240,7 @@ CGFloat const PanelAnimationDuration = 0.2;
- (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended {
NSMutableArray *leftBarButtonItems = [NSMutableArray array]; 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]; [leftBarButtonItems addObject:self.backButton];
} }
if ((accessible && !extended) && self.leftPanelButton) { if ((accessible && !extended) && self.leftPanelButton) {
@ -250,7 +250,7 @@ CGFloat const PanelAnimationDuration = 0.2;
if (extraButtons) { if (extraButtons) {
[leftBarButtonItems addObjectsFromArray: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 { - (void)setLeftPanelIsAccessible:(BOOL)leftPanelIsAccessible forViewController:(UIViewController *)viewController updateNavigationButtons:(BOOL)updateNavigationButtons {
@ -422,7 +422,7 @@ CGFloat const PanelAnimationDuration = 0.2;
if (extraButtons) { if (extraButtons) {
[navigationItems addObjectsFromArray: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 { - (BOOL)shouldExtendRightPanel {
@ -857,6 +857,7 @@ CGFloat const PanelAnimationDuration = 0.2;
// The main view. // The main view.
NavigationController *navigationController = [NavigationController setupNavigationController]; NavigationController *navigationController = [NavigationController setupNavigationController];
navigationController.manager = self;
self.navigationController = navigationController; self.navigationController = navigationController;
UIView *mainView = navigationController.view; UIView *mainView = navigationController.view;
@ -988,9 +989,11 @@ CGFloat const PanelAnimationDuration = 0.2;
} }
- (void)resetDrawers { - (void)resetDrawers {
if ([self.navigationItemViewController respondsToSelector:@selector(splitViewDidReset)]) { if (!self.navigationItemViewController) { return; }
[((UIViewController<MVMCoreUIDetailViewProtocol> *)self.navigationItemViewController) splitViewDidReset]; if ([self navigationBarModelExists]) {
[self updateNavigationBarForViewController:self.navigationItemViewController];
} else { } else {
// Legacy
[self setLeftPanelIsAccessible:self.leftPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES]; [self setLeftPanelIsAccessible:self.leftPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES];
[self setRightPanelIsAccessible:self.rightPanelIsAccessible 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; return nil;
} }
- (BOOL)isAPanelShowing {
return fabs(self.mainViewLeading.constant) > 1;
}
#pragma mark - Getters #pragma mark - Getters
// Returns the desired view or falls back. Hot fix until we can get away from using these functions... // 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 { + (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 { + (CGFloat)getApplicationViewMaxSize {
@ -1062,10 +1073,7 @@ CGFloat const PanelAnimationDuration = 0.2;
} }
// if it is not presented viewcontroller, existing BAU logic will be working // if it is not presented viewcontroller, existing BAU logic will be working
if (!viewController) { if (!viewController) {
viewController = self.navigationController.topViewController; viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController];
if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) {
viewController = [viewController performSelector:@selector(getCurrentViewController)];
}
} }
return viewController; return viewController;
} }
@ -1073,10 +1081,7 @@ CGFloat const PanelAnimationDuration = 0.2;
- (UIViewController *)getCurrentDetailViewController { - (UIViewController *)getCurrentDetailViewController {
__block UIViewController *viewController = nil; __block UIViewController *viewController = nil;
[MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
viewController = self.navigationController.topViewController; viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController];
if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) {
viewController = [viewController performSelector:@selector(getCurrentViewController)];
}
}]; }];
return viewController; return viewController;
} }

View File

@ -53,25 +53,22 @@ open class Container: View, ContainerProtocol {
open func constrainView(_ view: UIView) { open func constrainView(_ view: UIView) {
containerHelper.constrainView(view) containerHelper.constrainView(view)
} }
}
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
public extension Container { open override func updateView(_ size: CGFloat) {
override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
(view as? MVMCoreViewProtocol)?.updateView(size) (view as? MVMCoreViewProtocol)?.updateView(size)
containerHelper.updateViewMargins(self, model: containerModel, size: size) containerHelper.updateViewMargins(self, model: containerModel, size: size)
} }
/// Will be called only once. /// Will be called only once.
override func setupView() { open override func setupView() {
super.setupView() super.setupView()
isAccessibilityElement = false isAccessibilityElement = false
backgroundColor = .clear backgroundColor = .clear
} }
func addAndContain(_ view: UIView) { open func addAndContain(_ view: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view) addSubview(view)
containerHelper.constrainView(view) containerHelper.constrainView(view)

View File

@ -20,230 +20,228 @@ open class CoreUIModelMapping: ModelMapping {
open class func registerMolecules() { open class func registerMolecules() {
// MARK:- Stacks // MARK:- Stacks
try? ModelRegistry.register(handler: MoleculeStackView.self, for: StackModel.self) ModelRegistry.register(handler: MoleculeStackView.self, for: StackModel.self)
try? ModelRegistry.register(handler: UnOrderedList.self, for: UnOrderedListModel.self) ModelRegistry.register(handler: UnOrderedList.self, for: UnOrderedListModel.self)
try? ModelRegistry.register(handler: NumberedList.self, for: NumberedListModel.self) ModelRegistry.register(handler: NumberedList.self, for: NumberedListModel.self)
// MARK:- Label // MARK:- Label
try? ModelRegistry.register(handler: Label.self, for: LabelModel.self) ModelRegistry.register(handler: Label.self, for: LabelModel.self)
// MARK:- TextView // MARK:- TextView
try? ModelRegistry.register(handler: TextViewEntryField.self, for: TextViewEntryFieldModel.self) ModelRegistry.register(handler: TextViewEntryField.self, for: TextViewEntryFieldModel.self)
// MARK:- Buttons // MARK:- Buttons
try? ModelRegistry.register(handler: PillButton.self, for: ButtonModel.self) ModelRegistry.register(handler: PillButton.self, for: ButtonModel.self)
try? ModelRegistry.register(handler: TwoButtonView.self, for: TwoButtonViewModel.self) ModelRegistry.register(handler: TwoButtonView.self, for: TwoButtonViewModel.self)
try? ModelRegistry.register(handler: ExternalLink.self, for: ExternalLinkModel.self) ModelRegistry.register(handler: ExternalLink.self, for: ExternalLinkModel.self)
try? ModelRegistry.register(handler: Link.self, for: LinkModel.self) ModelRegistry.register(handler: Link.self, for: LinkModel.self)
try? ModelRegistry.register(handler: CaretLink.self, for: CaretLinkModel.self) ModelRegistry.register(handler: CaretLink.self, for: CaretLinkModel.self)
// MARK:- Entry Field // MARK:- Entry Field
try? ModelRegistry.register(handler: TextEntryField.self, for: TextEntryFieldModel.self) ModelRegistry.register(handler: TextEntryField.self, for: TextEntryFieldModel.self)
try? ModelRegistry.register(handler: MdnEntryField.self, for: MdnEntryFieldModel.self) ModelRegistry.register(handler: MdnEntryField.self, for: MdnEntryFieldModel.self)
try? ModelRegistry.register(handler: DigitEntryField.self, for: DigitEntryFieldModel.self) ModelRegistry.register(handler: DigitEntryField.self, for: DigitEntryFieldModel.self)
try? ModelRegistry.register(handler: ItemDropdownEntryField.self, for: ItemDropdownEntryFieldModel.self) ModelRegistry.register(handler: ItemDropdownEntryField.self, for: ItemDropdownEntryFieldModel.self)
try? ModelRegistry.register(handler: DateDropdownEntryField.self, for: DateDropdownEntryFieldModel.self) ModelRegistry.register(handler: DateDropdownEntryField.self, for: DateDropdownEntryFieldModel.self)
try? ModelRegistry.register(handler: MultiItemDropdownEntryField.self, for: MultiItemDropdownEntryFieldModel.self) ModelRegistry.register(handler: MultiItemDropdownEntryField.self, for: MultiItemDropdownEntryFieldModel.self)
// MARK:- Selectors // MARK:- Selectors
try? ModelRegistry.register(handler: RadioButton.self, for: RadioButtonModel.self) ModelRegistry.register(handler: RadioButton.self, for: RadioButtonModel.self)
try? ModelRegistry.register(handler: RadioBoxes.self, for: RadioBoxesModel.self) ModelRegistry.register(handler: RadioBoxes.self, for: RadioBoxesModel.self)
try? ModelRegistry.register(handler: Checkbox.self, for: CheckboxModel.self) ModelRegistry.register(handler: Checkbox.self, for: CheckboxModel.self)
try? ModelRegistry.register(handler: RadioSwatches.self, for: RadioSwatchesModel.self) ModelRegistry.register(handler: RadioSwatches.self, for: RadioSwatchesModel.self)
try? ModelRegistry.register(handler: Tags.self, for: TagsModel.self) ModelRegistry.register(handler: Tags.self, for: TagsModel.self)
try? ModelRegistry.register(handler: Tag.self, for: TagModel.self) ModelRegistry.register(handler: Tag.self, for: TagModel.self)
try? ModelRegistry.register(handler: Heart.self, for: HeartModel.self) ModelRegistry.register(handler: Heart.self, for: HeartModel.self)
try? ModelRegistry.register(handler: Stars.self, for: StarsModel.self) ModelRegistry.register(handler: Stars.self, for: StarsModel.self)
try? ModelRegistry.register(handler: Star.self, for: StarModel.self) ModelRegistry.register(handler: Star.self, for: StarModel.self)
// MARK:- Other Atoms // MARK:- Other Atoms
try? ModelRegistry.register(handler: ProgressBar.self, for: ProgressBarModel.self) ModelRegistry.register(handler: ProgressBar.self, for: ProgressBarModel.self)
try? ModelRegistry.register(handler: MultiProgress.self, for: MultiProgressBarModel.self) ModelRegistry.register(handler: MultiProgress.self, for: MultiProgressBarModel.self)
try? ModelRegistry.register(handler: CaretView.self, for: CaretViewModel.self) ModelRegistry.register(handler: CaretView.self, for: CaretViewModel.self)
try? ModelRegistry.register(handler: DashLine.self, for: DashLineModel.self) ModelRegistry.register(handler: DashLine.self, for: DashLineModel.self)
try? ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self) ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self)
try? ModelRegistry.register(handler: Line.self, for: LineModel.self) ModelRegistry.register(handler: Line.self, for: LineModel.self)
try? ModelRegistry.register(handler: Wheel.self, for: WheelModel.self) ModelRegistry.register(handler: Wheel.self, for: WheelModel.self)
try? ModelRegistry.register(handler: Toggle.self, for: ToggleModel.self) ModelRegistry.register(handler: Toggle.self, for: ToggleModel.self)
try? ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self) ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self)
try? ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self) ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self)
try? ModelRegistry.register(handler: RadioButtonLabel.self, for: RadioButtonLabelModel.self) ModelRegistry.register(handler: RadioButtonLabel.self, for: RadioButtonLabelModel.self)
try? ModelRegistry.register(handler: WebView.self, for: WebViewModel.self) ModelRegistry.register(handler: WebView.self, for: WebViewModel.self)
try? ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self) ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self)
try? ModelRegistry.register(handler: Video.self, for: VideoModel.self) ModelRegistry.register(handler: Video.self, for: VideoModel.self)
// MARK:- Horizontal Combination Molecules // MARK:- Horizontal Combination Molecules
try? ModelRegistry.register(handler: StringAndMoleculeView.self, for: StringAndMoleculeModel.self) ModelRegistry.register(handler: StringAndMoleculeView.self, for: StringAndMoleculeModel.self)
try? ModelRegistry.register(handler: ImageHeadlineBody.self, for: ImageHeadlineBodyModel.self) ModelRegistry.register(handler: ImageHeadlineBody.self, for: ImageHeadlineBodyModel.self)
try? ModelRegistry.register(handler: Tabs.self, for: TabsModel.self) ModelRegistry.register(handler: Tabs.self, for: TabsModel.self)
try? ModelRegistry.register(handler: TwoLinkView.self, for: TwoLinkViewModel.self) ModelRegistry.register(handler: TwoLinkView.self, for: TwoLinkViewModel.self)
// MARK:- Vertical Combination Molecules // MARK:- Vertical Combination Molecules
try? ModelRegistry.register(handler: HeadlineBody.self, for: HeadlineBodyModel.self) ModelRegistry.register(handler: HeadlineBody.self, for: HeadlineBodyModel.self)
try? ModelRegistry.register(handler: HeadLineBodyCaretLinkImage.self, for: HeadlineBodyCaretLinkImageModel.self) ModelRegistry.register(handler: HeadLineBodyCaretLinkImage.self, for: HeadlineBodyCaretLinkImageModel.self)
try? ModelRegistry.register(handler: EyebrowHeadlineBodyLink.self, for: EyebrowHeadlineBodyLinkModel.self) ModelRegistry.register(handler: EyebrowHeadlineBodyLink.self, for: EyebrowHeadlineBodyLinkModel.self)
try? ModelRegistry.register(handler: HeadlineBodyLink.self, for: HeadlineBodyLinkModel.self) ModelRegistry.register(handler: HeadlineBodyLink.self, for: HeadlineBodyLinkModel.self)
try? ModelRegistry.register(handler: HeadlineBodyButton.self, for: HeadlineBodyButtonModel.self) ModelRegistry.register(handler: HeadlineBodyButton.self, for: HeadlineBodyButtonModel.self)
try? ModelRegistry.register(handler: BGImageHeadlineBodyButton.self, for: BGImageHeadlineBodyButtonModel.self) ModelRegistry.register(handler: BGImageHeadlineBodyButton.self, for: BGImageHeadlineBodyButtonModel.self)
try? ModelRegistry.register(handler: ThreeHeadlineBodyLink.self, for: ThreeHeadlineBodyLinkModel.self) ModelRegistry.register(handler: ThreeHeadlineBodyLink.self, for: ThreeHeadlineBodyLinkModel.self)
ModelRegistry.register(handler: ImageButton.self, for: ImageButtonModel.self)
// MARK:- Left Right Molecules // MARK:- Left Right Molecules
try? ModelRegistry.register(handler: CornerLabels.self, for: CornerLabelsModel.self) ModelRegistry.register(handler: CornerLabels.self, for: CornerLabelsModel.self)
try? ModelRegistry.register(handler: LeftRightLabelView.self, for: LeftRightLabelModel.self) ModelRegistry.register(handler: LeftRightLabelView.self, for: LeftRightLabelModel.self)
try? ModelRegistry.register(handler: LabelToggle.self, for: LabelToggleModel.self) ModelRegistry.register(handler: LabelToggle.self, for: LabelToggleModel.self)
try? ModelRegistry.register(handler: HeadlineBodyToggle.self, for: HeadlineBodyToggleModel.self) ModelRegistry.register(handler: HeadlineBodyToggle.self, for: HeadlineBodyToggleModel.self)
try? ModelRegistry.register(handler: HeadlineBodyLinkToggle.self, for: HeadlineBodyLinkToggleModel.self) ModelRegistry.register(handler: HeadlineBodyLinkToggle.self, for: HeadlineBodyLinkToggleModel.self)
try? ModelRegistry.register(handler: ActionDetailWithImage.self, for: ActionDetailWithImageModel.self) ModelRegistry.register(handler: ActionDetailWithImage.self, for: ActionDetailWithImageModel.self)
// MARK:- List items // MARK:- List items
try? ModelRegistry.register(handler: MoleculeTableViewCell.self, for: MoleculeListItemModel.self) ModelRegistry.register(handler: MoleculeTableViewCell.self, for: MoleculeListItemModel.self)
try? ModelRegistry.register(handler: DropDownFilterTableViewCell.self, for: DropDownListItemModel.self) ModelRegistry.register(handler: DropDownFilterTableViewCell.self, for: DropDownListItemModel.self)
try? ModelRegistry.register(handler: AccordionMoleculeTableViewCell.self, for: AccordionListItemModel.self) ModelRegistry.register(handler: AccordionMoleculeTableViewCell.self, for: AccordionListItemModel.self)
try? ModelRegistry.register(handler: TabsTableViewCell.self, for: TabsListItemModel.self) ModelRegistry.register(handler: TabsTableViewCell.self, for: TabsListItemModel.self)
try? ModelRegistry.register(handler: ListProgressBarData.self, for: ListProgressBarDataModel.self) ModelRegistry.register(handler: ListProgressBarData.self, for: ListProgressBarDataModel.self)
// MARK:- Other Items // MARK:- Other Items
try? ModelRegistry.register(handler: MoleculeStackItem.self, for: MoleculeStackItemModel.self) ModelRegistry.register(handler: MoleculeStackItem.self, for: MoleculeStackItemModel.self)
try? ModelRegistry.register(handler: StackItem.self, for: StackItemModel.self) ModelRegistry.register(handler: StackItem.self, for: StackItemModel.self)
try? ModelRegistry.register(handler: MoleculeCollectionViewCell.self, for: MoleculeCollectionItemModel.self) ModelRegistry.register(handler: MoleculeCollectionViewCell.self, for: MoleculeCollectionItemModel.self)
try? ModelRegistry.register(handler: CarouselItem.self, for: CarouselItemModel.self) ModelRegistry.register(handler: CarouselItem.self, for: CarouselItemModel.self)
// MARK:- Other Container Molecules // MARK:- Other Container Molecules
try? ModelRegistry.register(handler: MoleculeContainer.self, for: MoleculeContainerModel.self) ModelRegistry.register(handler: MoleculeContainer.self, for: MoleculeContainerModel.self)
try? ModelRegistry.register(handler: MoleculeHeaderView.self, for: MoleculeHeaderModel.self) ModelRegistry.register(handler: MoleculeHeaderView.self, for: MoleculeHeaderModel.self)
try? ModelRegistry.register(handler: FooterView.self, for: FooterModel.self) ModelRegistry.register(handler: FooterView.self, for: FooterModel.self)
try? ModelRegistry.register(handler: Scroller.self, for: ScrollerModel.self) ModelRegistry.register(handler: Scroller.self, for: ScrollerModel.self)
try? ModelRegistry.register(handler: ModuleMolecule.self, for: ModuleMoleculeModel.self) ModelRegistry.register(handler: ModuleMolecule.self, for: ModuleMoleculeModel.self)
try? ModelRegistry.register(handler: BGImageMolecule.self, for: BGImageMoleculeModel.self) ModelRegistry.register(handler: BGImageMolecule.self, for: BGImageMoleculeModel.self)
try? ModelRegistry.register(handler: BGVideoImageMolecule.self, for: BGVideoImageMoleculeModel.self) ModelRegistry.register(handler: BGVideoImageMolecule.self, for: BGVideoImageMoleculeModel.self)
try? ModelRegistry.register(handler: MoleculeSectionHeader.self, for: MoleculeSectionHeaderModel.self) ModelRegistry.register(handler: MoleculeSectionHeader.self, for: MoleculeSectionHeaderModel.self)
try? ModelRegistry.register(handler: MoleculeSectionFooter.self, for: MoleculeSectionFooterModel.self) ModelRegistry.register(handler: MoleculeSectionFooter.self, for: MoleculeSectionFooterModel.self)
// MARK:- Other Molecules // MARK:- Other Molecules
try? ModelRegistry.register(handler: DoughnutChartView.self, for: DoughnutChartModel.self) ModelRegistry.register(handler: DoughnutChartView.self, for: DoughnutChartModel.self)
// Navigation Molecules // Navigation Molecules
try? ModelRegistry.register(NavigationItemModel.self) ModelRegistry.register(NavigationItemModel.self)
try? ModelRegistry.register(NavigationImageButtonModel.self) ModelRegistry.register(NavigationImageButtonModel.self)
try? ModelRegistry.register(NavigationLabelButtonModel.self) ModelRegistry.register(NavigationLabelButtonModel.self)
// MARK:- Other Organisms // MARK:- Other Organisms
try? ModelRegistry.register(handler: Carousel.self, for: CarouselModel.self) ModelRegistry.register(handler: Carousel.self, for: CarouselModel.self)
try? ModelRegistry.register(handler: BarsIndicatorView.self, for: BarsCarouselIndicatorModel.self) ModelRegistry.register(handler: BarsIndicatorView.self, for: BarsCarouselIndicatorModel.self)
try? ModelRegistry.register(handler: NumericIndicatorView.self, for: NumericCarouselIndicatorModel.self) ModelRegistry.register(handler: NumericIndicatorView.self, for: NumericCarouselIndicatorModel.self)
// MARK:- Designed List Items // MARK:- Designed List Items
try? ModelRegistry.register(handler: ListLeftVariableIconWithRightCaret.self, for: ListLeftVariableIconWithRightCaretModel.self) ModelRegistry.register(handler: ListLeftVariableIconWithRightCaret.self, for: ListLeftVariableIconWithRightCaretModel.self)
try? ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretBodyText.self, for: ListLeftVariableIconWithRightCaretBodyTextModel.self) ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretBodyText.self, for: ListLeftVariableIconWithRightCaretBodyTextModel.self)
try? ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretAllTextLinks.self, for: ListLeftVariableIconWithRightCaretAllTextLinksModel.self) ModelRegistry.register(handler: ListLeftVariableIconWithRightCaretAllTextLinks.self, for: ListLeftVariableIconWithRightCaretAllTextLinksModel.self)
try? ModelRegistry.register(handler: ListLeftVariableCheckboxAllTextAndLinks.self, for: ListLeftVariableCheckboxAllTextAndLinksModel.self) ModelRegistry.register(handler: ListLeftVariableCheckboxAllTextAndLinks.self, for: ListLeftVariableCheckboxAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListLeftVariableRadioButtonAndPaymentMethod.self, for: ListLeftVariableRadioButtonAndPaymentMethodModel.self) ModelRegistry.register(handler: ListLeftVariableRadioButtonAndPaymentMethod.self, for: ListLeftVariableRadioButtonAndPaymentMethodModel.self)
try? ModelRegistry.register(handler: ListLeftVariableRadioButtonBodyText.self, for: ListLeftVariableRadioButtonBodyTextModel.self) ModelRegistry.register(handler: ListLeftVariableRadioButtonBodyText.self, for: ListLeftVariableRadioButtonBodyTextModel.self)
try? ModelRegistry.register(handler: ListLeftVariableRadioButtonAllTextAndLinks.self, for: ListLeftVariableRadioButtonAllTextAndLinksModel.self) ModelRegistry.register(handler: ListLeftVariableRadioButtonAllTextAndLinks.self, for: ListLeftVariableRadioButtonAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListLeftVariableCheckboxBodyText.self, for: ListLeftVariableCheckboxBodyTextModel.self) ModelRegistry.register(handler: ListLeftVariableCheckboxBodyText.self, for: ListLeftVariableCheckboxBodyTextModel.self)
try? ModelRegistry.register(handler: ListLeftVariableIconAllTextLinks.self, for: ListLeftVariableIconAllTextLinksModel.self) ModelRegistry.register(handler: ListLeftVariableIconAllTextLinks.self, for: ListLeftVariableIconAllTextLinksModel.self)
try? ModelRegistry.register(handler: ListLeftVariableNumberedListAllTextAndLinks.self, for: ListLeftVariableNumberedListAllTextAndLinksModel.self) ModelRegistry.register(handler: ListLeftVariableNumberedListAllTextAndLinks.self, for: ListLeftVariableNumberedListAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListLeftVariableNumberedListBodyText.self, for: ListLeftVariableNumberedListBodyTextModel.self) ModelRegistry.register(handler: ListLeftVariableNumberedListBodyText.self, for: ListLeftVariableNumberedListBodyTextModel.self)
try? ModelRegistry.register(handler: ListRVWheel.self, for: ListRVWheelModel.self) ModelRegistry.register(handler: ListRVWheel.self, for: ListRVWheelModel.self)
try? ModelRegistry.register(handler: ListRightVariablePayments.self, for: ListRightVariablePaymentsModel.self) ModelRegistry.register(handler: ListRightVariablePayments.self, for: ListRightVariablePaymentsModel.self)
try? ModelRegistry.register(handler: ListRightVariableTotalData.self, for: ListRightVariableTotalDataModel.self) ModelRegistry.register(handler: ListRightVariableTotalData.self, for: ListRightVariableTotalDataModel.self)
try? ModelRegistry.register(handler: ListRightVariableTextLinkAllTextAndLinks.self, for: ListRightVariableTextLinkAllTextAndLinksModel.self) ModelRegistry.register(handler: ListRightVariableTextLinkAllTextAndLinks.self, for: ListRightVariableTextLinkAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListRightVariableButtonAllTextAndLinks.self, for: ListRightVariableButtonAllTextAndLinksModel.self) ModelRegistry.register(handler: ListRightVariableButtonAllTextAndLinks.self, for: ListRightVariableButtonAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListRightVariablePriceChangeBodyText.self, for: ListRightVariablePriceChangeBodyTextModel.self) ModelRegistry.register(handler: ListRightVariablePriceChangeBodyText.self, for: ListRightVariablePriceChangeBodyTextModel.self)
try? ModelRegistry.register(handler: ListRightVariablePriceChangeAllTextAndLinks.self, for: ListRightVariablePriceChangeAllTextAndLinksModel.self) ModelRegistry.register(handler: ListRightVariablePriceChangeAllTextAndLinks.self, for: ListRightVariablePriceChangeAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListRightVariableToggleAllTextAndLinks.self, for: ListRightVariableToggleAllTextAndLinksModel.self) ModelRegistry.register(handler: ListRightVariableToggleAllTextAndLinks.self, for: ListRightVariableToggleAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListRightVariableRightCaretAllTextAndLinks.self, for: ListRightVariableRightCaretAllTextAndLinksModel.self) ModelRegistry.register(handler: ListRightVariableRightCaretAllTextAndLinks.self, for: ListRightVariableRightCaretAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListOneColumnFullWidthTextAllTextAndLinks.self, for: ListOneColumnFullWidthTextAllTextAndLinksModel.self) ModelRegistry.register(handler: ListOneColumnFullWidthTextAllTextAndLinks.self, for: ListOneColumnFullWidthTextAllTextAndLinksModel.self)
try? ModelRegistry.register(handler: ListOneColumnFullWidthTextBodyText.self, for: ListOneColumnFullWidthTextBodyTextModel.self) ModelRegistry.register(handler: ListOneColumnFullWidthTextBodyText.self, for: ListOneColumnFullWidthTextBodyTextModel.self)
try? ModelRegistry.register(handler: ListTwoColumnCompareChanges.self, for: ListTwoColumnCompareChangesModel.self) ModelRegistry.register(handler: ListTwoColumnCompareChanges.self, for: ListTwoColumnCompareChangesModel.self)
try? ModelRegistry.register(handler: ListTwoColumnPriceDetails.self, for: ListTwoColumnPriceDetailsModel.self) ModelRegistry.register(handler: ListTwoColumnPriceDetails.self, for: ListTwoColumnPriceDetailsModel.self)
try? ModelRegistry.register(handler: ListTwoColumnPriceDescription.self, for: ListTwoColumnPriceDescriptionModel.self) ModelRegistry.register(handler: ListTwoColumnPriceDescription.self, for: ListTwoColumnPriceDescriptionModel.self)
try? ModelRegistry.register(handler: ListTwoColumnDropdownSelectors.self, for: ListTwoColumnDropdownSelectorsModel.self) ModelRegistry.register(handler: ListTwoColumnDropdownSelectors.self, for: ListTwoColumnDropdownSelectorsModel.self)
try? ModelRegistry.register(handler: ListThreeColumnInternationalData.self, for: ListThreeColumnInternationalDataModel.self) ModelRegistry.register(handler: ListThreeColumnInternationalData.self, for: ListThreeColumnInternationalDataModel.self)
try? ModelRegistry.register(handler: ListThreeColumnDataUsage.self, for: ListThreeColumnDataUsageModel.self) ModelRegistry.register(handler: ListThreeColumnDataUsage.self, for: ListThreeColumnDataUsageModel.self)
try? ModelRegistry.register(handler: ListThreeColumnBillChanges.self, for: ListThreeColumnBillChangesModel.self) ModelRegistry.register(handler: ListThreeColumnBillChanges.self, for: ListThreeColumnBillChangesModel.self)
try? ModelRegistry.register(handler: ListThreeColumnBillHistory.self, for: ListThreeColumnBillHistoryModel.self) ModelRegistry.register(handler: ListThreeColumnBillHistory.self, for: ListThreeColumnBillHistoryModel.self)
try? ModelRegistry.register(handler: ListThreeColumnSpeedTest.self, for: ListThreeColumnSpeedTestModel.self) ModelRegistry.register(handler: ListThreeColumnSpeedTest.self, for: ListThreeColumnSpeedTestModel.self)
try? ModelRegistry.register(handler: ListFourColumnDataUsageListItem.self, for: ListFourColumnDataUsageListItemModel.self) ModelRegistry.register(handler: ListFourColumnDataUsageListItem.self, for: ListFourColumnDataUsageListItemModel.self)
try? ModelRegistry.register(handler: ListProgressBarThin.self, for: ListProgressBarThinModel.self) ModelRegistry.register(handler: ListProgressBarThin.self, for: ListProgressBarThinModel.self)
try? ModelRegistry.register(handler: ListStoreLocator.self, for: ListStoreLocatorModel.self) ModelRegistry.register(handler: ListStoreLocator.self, for: ListStoreLocatorModel.self)
try? ModelRegistry.register(handler: ListStarRating.self, for: ListStarRatingModel.self) ModelRegistry.register(handler: ListStarRating.self, for: ListStarRatingModel.self)
// MARK:- Designed Section Dividers // MARK:- Designed Section Dividers
try? ModelRegistry.register(handler: ListFourColumnDataUsageDivider.self, for: ListFourColumnDataUsageDividerModel.self) ModelRegistry.register(handler: ListFourColumnDataUsageDivider.self, for: ListFourColumnDataUsageDividerModel.self)
try? ModelRegistry.register(handler: ListThreeColumnPlanDataDivider.self, for: ListThreeColumnPlanDataDividerModel.self) ModelRegistry.register(handler: ListThreeColumnPlanDataDivider.self, for: ListThreeColumnPlanDataDividerModel.self)
try? ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerShort.self, for: ListOneColumnTextWithWhitespaceDividerShortModel.self) ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerShort.self, for: ListOneColumnTextWithWhitespaceDividerShortModel.self)
try? ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerTall.self, for: ListOneColumnTextWithWhitespaceDividerTallModel.self) ModelRegistry.register(handler: ListOneColumnTextWithWhitespaceDividerTall.self, for: ListOneColumnTextWithWhitespaceDividerTallModel.self)
try? ModelRegistry.register(handler: ListOneColumnFullWidthTextDividerSubsection.self, for: ListOneColumnFullWidthTextDividerSubsectionModel.self) ModelRegistry.register(handler: ListOneColumnFullWidthTextDividerSubsection.self, for: ListOneColumnFullWidthTextDividerSubsectionModel.self)
try? ModelRegistry.register(handler: ListTwoColumnSubsectionDivider.self, for: ListTwoColumnSubsectionDividerModel.self) ModelRegistry.register(handler: ListTwoColumnSubsectionDivider.self, for: ListTwoColumnSubsectionDividerModel.self)
try? ModelRegistry.register(handler: ListThreeColumnInternationalDataDivider.self, for: ListThreeColumnInternationalDataDividerModel.self) ModelRegistry.register(handler: ListThreeColumnInternationalDataDivider.self, for: ListThreeColumnInternationalDataDividerModel.self)
try? ModelRegistry.register(handler: ListThreeColumnSpeedTestDivider.self, for: ListThreeColumnSpeedTestDividerModel.self) ModelRegistry.register(handler: ListThreeColumnSpeedTestDivider.self, for: ListThreeColumnSpeedTestDividerModel.self)
try? ModelRegistry.register(handler: ListThreeColumnBillChangesDivider.self, for: ListThreeColumnBillChangesDividerModel.self) ModelRegistry.register(handler: ListThreeColumnBillChangesDivider.self, for: ListThreeColumnBillChangesDividerModel.self)
try? ModelRegistry.register(handler: ListThreeColumnDataUsageDivider.self, for: ListThreeColumnDataUsageDividerModel.self) ModelRegistry.register(handler: ListThreeColumnDataUsageDivider.self, for: ListThreeColumnDataUsageDividerModel.self)
try? ModelRegistry.register(handler: ListThreeColumnBillHistoryDivider.self, for: ListThreeColumnBillHistoryDividerModel.self) ModelRegistry.register(handler: ListThreeColumnBillHistoryDivider.self, for: ListThreeColumnBillHistoryDividerModel.self)
// MARK:- Designed Headers // MARK:- Designed Headers
try? ModelRegistry.register(handler: HeadersH1Button.self, for: HeadersH1ButtonModel.self) ModelRegistry.register(handler: HeadersH1Button.self, for: HeadersH1ButtonModel.self)
try? ModelRegistry.register(handler: HeadersH1LandingPageHeader.self, for: HeadersH1LandingPageHeaderModel.self) ModelRegistry.register(handler: HeadersH1LandingPageHeader.self, for: HeadersH1LandingPageHeaderModel.self)
try? ModelRegistry.register(handler: HeadersH1NoButtonsBodyText.self, for: HeadersH1NoButtonsBodyTextModel.self) ModelRegistry.register(handler: HeadersH1NoButtonsBodyText.self, for: HeadersH1NoButtonsBodyTextModel.self)
try? ModelRegistry.register(handler: HeadersH2NoButtonsBodyText.self, for: HeadersH2NoButtonsBodyTextModel.self) ModelRegistry.register(handler: HeadersH2NoButtonsBodyText.self, for: HeadersH2NoButtonsBodyTextModel.self)
try? ModelRegistry.register(handler: HeadersH2TinyButton.self, for: HeadersH2TinyButtonModel.self) ModelRegistry.register(handler: HeadersH2TinyButton.self, for: HeadersH2TinyButtonModel.self)
try? ModelRegistry.register(handler: HeadersH2Buttons.self, for: HeadersH2ButtonsModel.self) ModelRegistry.register(handler: HeadersH2Buttons.self, for: HeadersH2ButtonsModel.self)
try? ModelRegistry.register(handler: HeadersH2PricingTwoRows.self, for: HeadersH2PricingTwoRowsModel.self) ModelRegistry.register(handler: HeadersH2PricingTwoRows.self, for: HeadersH2PricingTwoRowsModel.self)
try? ModelRegistry.register(handler: HeadersH2Link.self, for: HeadersH2LinkModel.self) ModelRegistry.register(handler: HeadersH2Link.self, for: HeadersH2LinkModel.self)
try? ModelRegistry.register(handler: HeadersH2CaretLink.self, for: HeadersH2CaretLinkModel.self) ModelRegistry.register(handler: HeadersH2CaretLink.self, for: HeadersH2CaretLinkModel.self)
// MARK:- Device Items // MARK:- Device Items
try? ModelRegistry.register(handler: ListDeviceComplexButtonMedium.self, for: ListDeviceComplexButtonMediumModel.self) ModelRegistry.register(handler: ListDeviceComplexButtonMedium.self, for: ListDeviceComplexButtonMediumModel.self)
try? ModelRegistry.register(handler: ListDeviceComplexButtonSmall.self, for: ListDeviceComplexButtonSmallModel.self) ModelRegistry.register(handler: ListDeviceComplexButtonSmall.self, for: ListDeviceComplexButtonSmallModel.self)
try? ModelRegistry.register(handler: ListDeviceComplexLinkSmall.self, for: ListDeviceComplexLinkSmallModel.self) ModelRegistry.register(handler: ListDeviceComplexLinkSmall.self, for: ListDeviceComplexLinkSmallModel.self)
try? ModelRegistry.register(handler: ListDeviceComplexLinkMedium.self, for: ListDeviceComplexLinkMediumModel.self) ModelRegistry.register(handler: ListDeviceComplexLinkMedium.self, for: ListDeviceComplexLinkMediumModel.self)
// MARK:- LockUps // MARK:- LockUps
try? ModelRegistry.register(handler: LockUpsPlanNames.self, for: LockUpsPlanNamesModel.self) ModelRegistry.register(handler: LockUpsPlanNames.self, for: LockUpsPlanNamesModel.self)
try? ModelRegistry.register(handler: LockupsPlanSMLXL.self, for: LockupsPlanSMLXLModel.self) ModelRegistry.register(handler: LockupsPlanSMLXL.self, for: LockupsPlanSMLXLModel.self)
// MARK: - Top Notifications // MARK: - Top Notifications
try? ModelRegistry.register(handler: NotificationView.self, for: NotificationModel.self) ModelRegistry.register(handler: NotificationView.self, for: NotificationModel.self)
try? ModelRegistry.register(handler: CollapsableNotification.self, for: CollapsableNotificationModel.self) ModelRegistry.register(handler: CollapsableNotification.self, for: CollapsableNotificationModel.self)
} }
open class func registerLabelAttributes() { open class func registerLabelAttributes() {
try? ModelRegistry.register(LabelAttributeFontModel.self) ModelRegistry.register(LabelAttributeFontModel.self)
try? ModelRegistry.register(LabelAttributeColorModel.self) ModelRegistry.register(LabelAttributeColorModel.self)
try? ModelRegistry.register(LabelAttributeImageModel.self) ModelRegistry.register(LabelAttributeImageModel.self)
try? ModelRegistry.register(LabelAttributeUnderlineModel.self) ModelRegistry.register(LabelAttributeUnderlineModel.self)
try? ModelRegistry.register(LabelAttributeStrikeThroughModel.self) ModelRegistry.register(LabelAttributeStrikeThroughModel.self)
try? ModelRegistry.register(LabelAttributeActionModel.self) ModelRegistry.register(LabelAttributeActionModel.self)
} }
open class func registerBehaviors() { open class func registerBehaviors() {
try? ModelRegistry.register(handler: ScreenBrightnessModifierBehavior.self, for: ScreenBrightnessModifierBehaviorModel.self) ModelRegistry.register(handler: ScreenBrightnessModifierBehavior.self, for: ScreenBrightnessModifierBehaviorModel.self)
try? ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self) ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self)
try? ModelRegistry.register(handler: AddRemoveMoleculeBehavior.self, for: AddRemoveMoleculeBehaviorModel.self) ModelRegistry.register(handler: AddRemoveMoleculeBehavior.self, for: AddRemoveMoleculeBehaviorModel.self)
} }
open override class func registerActions() { open override class func registerActions() {
super.registerActions() super.registerActions()
try? ModelRegistry.register(ActionPopupModel.self) ModelRegistry.register(ActionPopupModel.self)
try? ModelRegistry.register(ActionAlertModel.self) ModelRegistry.register(ActionAlertModel.self)
try? ModelRegistry.register(ActionTopAlertModel.self) ModelRegistry.register(ActionTopAlertModel.self)
try? ModelRegistry.register(ActionCollapseNotificationModel.self) ModelRegistry.register(ActionCollapseNotificationModel.self)
try? ModelRegistry.register(ActionOpenPanelModel.self) ModelRegistry.register(ActionOpenPanelModel.self)
try? ModelRegistry.register(ActionTopNotificationModel.self) ModelRegistry.register(ActionTopNotificationModel.self)
} }
open class func registerRules() { open class func registerRules() {
try? ModelRegistry.register(RuleRequiredModel.self) ModelRegistry.register(RuleRequiredModel.self)
try? ModelRegistry.register(RuleAnyRequiredModel.self) ModelRegistry.register(RuleAnyRequiredModel.self)
try? ModelRegistry.register(RuleAnyValueChangedModel.self) ModelRegistry.register(RuleAnyValueChangedModel.self)
try? ModelRegistry.register(RuleAllValueChangedModel.self) ModelRegistry.register(RuleAllValueChangedModel.self)
try? ModelRegistry.register(RuleEqualsModel.self) ModelRegistry.register(RuleEqualsModel.self)
try? ModelRegistry.register(RuleEqualsIgnoreCaseModel.self) ModelRegistry.register(RuleEqualsIgnoreCaseModel.self)
try? ModelRegistry.register(RuleRegexModel.self) ModelRegistry.register(RuleRegexModel.self)
} }
} }

View File

@ -32,7 +32,10 @@ NS_ASSUME_NONNULL_BEGIN
+ (UIEdgeInsets)getMarginsForView:(nullable UIView *)view; + (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. /// 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. /// Checks if the view or any descendents of the view is currently focused for voice over.
+ (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view; + (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view;

View File

@ -63,14 +63,19 @@
} }
// if it is not presented viewcontroller, existing BAU logic will be working // if it is not presented viewcontroller, existing BAU logic will be working
if (!viewController) { if (!viewController) {
viewController = [MVMCoreUISession sharedGlobal].navigationController.topViewController; viewController = [self getViewControllerTraversingManagers:[MVMCoreUISession sharedGlobal].navigationController.topViewController];
if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) {
viewController = [viewController performSelector:@selector(getCurrentViewController)];
}
} }
return viewController; 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 { + (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view {
if (!UIAccessibilityIsVoiceOverRunning()) { if (!UIAccessibilityIsVoiceOverRunning()) {
return NO; return NO;

31
Scripts/build_aggregate.sh Executable file
View File

@ -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

View File

@ -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

86
Scripts/download_framework.sh Executable file
View File

@ -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

View File

@ -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

89
Scripts/upload_framework.sh Executable file
View File

@ -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"