diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 97be87e4..3433e17b 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -293,6 +293,7 @@ AF1C336F2885A16A006B1001 /* ActionCollapseNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */; }; AF1C33712885AE76006B1001 /* MVMCoreUIActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */; }; AF1C33732885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */; }; + AF1CEFE82BEA73890001F9A5 /* VDSCoreTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */; }; AF60A7F62892D2E300919EEB /* ActionDismissNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */; }; AF60A7F82892D34D00919EEB /* ActionDismissNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */; }; AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; }; @@ -607,7 +608,6 @@ EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; }; EABFC152276913E800E78B40 /* FormLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* FormLabelModel.swift */; }; EACCF38C2ABB346700E0F104 /* VDS-Interpreters.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACCF38B2ABB346700E0F104 /* VDS-Interpreters.swift */; }; - EAD715AA2BBC8FAF00DEDA6A /* VDSTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAD715A92BBC8FAF00DEDA6A /* VDSTokens.xcframework */; }; FD99130028E21E4900542CC3 /* RuleNotEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */; }; /* End PBXBuildFile section */ @@ -911,6 +911,7 @@ AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationHandler.swift; sourceTree = ""; }; AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionHandler.swift; sourceTree = ""; }; AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionOpenPageHandler.swift; sourceTree = ""; }; + AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSCoreTokens.xcframework; path = ../SharedFrameworks/VDSCoreTokens.xcframework; sourceTree = ""; }; AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationModel.swift; sourceTree = ""; }; AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationHandler.swift; sourceTree = ""; }; AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = ""; }; @@ -1244,9 +1245,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + AF1CEFE82BEA73890001F9A5 /* VDSCoreTokens.xcframework in Frameworks */, D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */, EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */, - EAD715AA2BBC8FAF00DEDA6A /* VDSTokens.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2138,6 +2139,7 @@ D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = { isa = PBXGroup; children = ( + AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */, EAD715A92BBC8FAF00DEDA6A /* VDSTokens.xcframework */, EA985C5F2970A3F000F2FF2E /* VDS.framework */, D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift index ffeb8c36..85195649 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift @@ -7,7 +7,7 @@ // import UIKit -import VDSTokens +import VDSCoreTokens import VDS import MVMCore diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift index f9261d97..dcffc9e8 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift @@ -7,7 +7,7 @@ // import UIKit -import VDSTokens +import VDSCoreTokens import VDS open class Link: VDS.TextLink, VDSMoleculeViewProtocol { diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index 7334ccf5..986d4e72 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -7,7 +7,7 @@ // import UIKit -import VDSTokens +import VDSCoreTokens import VDS import MVMCore import Combine diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index 2f9ae80a..e73a8d5c 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -7,7 +7,7 @@ // import UIKit -import VDSTokens +import VDSCoreTokens @objcMembers open class RadioButton: Control, MFButtonProtocol { //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift index 89237662..50802ff9 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift @@ -41,7 +41,9 @@ open class ButtonIcon: VDS.ButtonIcon, VDSMoleculeViewProtocol { iconName = viewModel.iconName selectedIconName = viewModel.selectedIconName size = viewModel.size - customSize = viewModel.customSize + customContainerSize = viewModel.customContainerSize + customIconSize = viewModel.customIconSize + customBadgeIndicatorOffset = viewModel.customBadgeIndicatorOffSet floating = viewModel.floating fitToIcon = viewModel.fitToIcon hideBorder = viewModel.hideBorder diff --git a/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift index ed93e969..aefa9dfe 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift @@ -31,7 +31,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol { public var iconName: Icon.Name = .info public var selectedIconName: Icon.Name? public var size = ButtonIcon.Size.large - public var customSize : Int? + public var customContainerSize : Int? + public var customIconSize : Int? + public var customBadgeIndicatorOffSet : CGPoint? public var floating: Bool = false public var fitToIcon: Bool = false public var hideBorder: Bool = true @@ -85,7 +87,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol { case iconName case selectedIconName case size - case customSize + case customContainerSize + case customIconSize + case customBadgeIndicatorOffSet case floating case fitToIcon case hideBorder @@ -110,7 +114,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol { iconName = try container.decode(Icon.Name.self, forKey: .iconName) selectedIconName = try container.decodeIfPresent(Icon.Name.self, forKey: .selectedIconName) size = try container.decodeIfPresent(ButtonIcon.Size.self, forKey: .size) ?? .large - customSize = try container.decodeIfPresent(Int.self, forKey: .customSize) + customContainerSize = try container.decodeIfPresent(Int.self, forKey: .customContainerSize) + customIconSize = try container.decodeIfPresent(Int.self, forKey: .customIconSize) + customBadgeIndicatorOffSet = try container.decodeIfPresent(CGPoint.self, forKey: .customBadgeIndicatorOffSet) floating = try container.decodeIfPresent(Bool.self, forKey: .floating) ?? false fitToIcon = try container.decodeIfPresent(Bool.self, forKey: .fitToIcon) ?? false hideBorder = try container.decodeIfPresent(Bool.self, forKey: .hideBorder) ?? false @@ -131,7 +137,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol { try container.encode(iconName, forKey: .iconName) try container.encodeIfPresent(selectedIconName, forKey: .selectedIconName) try container.encodeIfPresent(size, forKey: .size) - try container.encodeIfPresent(customSize, forKey: .customSize) + try container.encodeIfPresent(customContainerSize, forKey: .customContainerSize) + try container.encodeIfPresent(customIconSize, forKey: .customIconSize) + try container.encodeIfPresent(customBadgeIndicatorOffSet, forKey: .customBadgeIndicatorOffSet) try container.encodeIfPresent(floating, forKey: .floating) try container.encodeIfPresent(fitToIcon, forKey: .fitToIcon) try container.encodeIfPresent(hideBorder, forKey: .hideBorder) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift index 56827bec..21cca600 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift @@ -5,7 +5,7 @@ // Created by Kevin Christiano on 1/30/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // -import VDSTokens +import VDSCoreTokens open class CarouselIndicator: Control, CarouselPageControlProtocol { //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift index d793dc98..d7381d64 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift @@ -7,7 +7,7 @@ // import Foundation -import VDSTokens +import VDSCoreTokens open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelProtocol, EnableableModelProtocol { //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Views/IconModel.swift b/MVMCoreUI/Atomic/Atoms/Views/IconModel.swift index 4fbbd712..ac8e6d9e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/IconModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/IconModel.swift @@ -8,7 +8,7 @@ import Foundation import VDS -import VDSTokens +import VDSCoreTokens open class IconModel: MoleculeModelProtocol { diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 6361f46b..39b445bc 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -375,8 +375,8 @@ extension Label { public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { guard let abstractContainer = label.abstractTextContainer() else { return CGRect() } - let textContainer = abstractContainer.0 - let layoutManager = abstractContainer.1 + let textContainer = abstractContainer.textContainer + let layoutManager = abstractContainer.layoutManager var glyphRange = NSRange() @@ -385,36 +385,6 @@ extension Label { return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer) } - - /** - Provides a text container and layout manager of how the text would appear on screen. - They are used in tandem to derive low-level TextKit results of the label. - */ - public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? { - - // Must configure the attributed string to translate what would appear on screen to accurately analyze. - guard let attributedText = attributedText else { return nil } - - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = textAlignment - - let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) - stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) - - let textStorage = NSTextStorage(attributedString: stagedAttributedString) - let layoutManager = NSLayoutManager() - let textContainer = NSTextContainer(size: .zero) - - layoutManager.addTextContainer(textContainer) - textStorage.addLayoutManager(layoutManager) - - textContainer.lineFragmentPadding = 0.0 - textContainer.lineBreakMode = lineBreakMode - textContainer.maximumNumberOfLines = numberOfLines - textContainer.size = bounds.size - - return (textContainer, layoutManager, textStorage) - } } // MARK: - Atomization diff --git a/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift index 8ac93a4f..4bdcefe0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift @@ -7,7 +7,7 @@ // import UIKit -import VDSTokens +import VDSCoreTokens import VDS public class LineModel: MoleculeModelProtocol, Invertable { diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index d500206c..b8b64f3d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -34,6 +34,7 @@ open class TileContainerModel: TileContainerBaseModel Bool { @@ -107,5 +111,6 @@ && peakingArrowColor == model.peakingArrowColor && enabled == model.enabled && readOnly == model.readOnly + && gone == model.gone } } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index 197c4f9f..f38e3d10 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -6,7 +6,7 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import VDSTokens +import VDSCoreTokens public enum NavigationItemStyle: String, Codable { case light diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index cef93935..5691390a 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -10,7 +10,7 @@ import Foundation import Combine import Dispatch import MVMCore -import VDSTokens +import VDSCoreTokens @objcMembers open class CollapsableNotification: View { //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift b/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift index 12399292..3ef117da 100644 --- a/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift @@ -90,7 +90,7 @@ open class Carousel: View { showPeaking(false) // Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled. - guard let model = model as? CarouselModel, !model.molecules.isEmpty else { return } + guard let model = model as? CarouselModel, !model.visibleMolecules.isEmpty else { return } guard (model.paging == true || loop == true) else { DispatchQueue.main.async { [self] in updatePagerVisibility() @@ -174,9 +174,12 @@ open class Carousel: View { MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] [\(ObjectIdentifier(self).hashValue)]\noriginal model: \(originalModel?.debugDescription ?? "none")\nnew model: \(model)") if #available(iOS 15.0, *) { - if let originalModel, carouselModel.isDeeplyVisuallyEquivalent(to: originalModel) { + if let originalModel, carouselModel.isDeeplyVisuallyEquivalent(to: originalModel), + originalModel.visibleMolecules.isVisuallyEquivalent(to: molecules ?? []) // Since the carousel model's children are in place replaced and we do not have a deep copy of this model tree, add in this hack to check if the prior captured carousel items match the newly visible ones. + { // Prevents a carousel reset while still updating the cell backing data through reconfigureItems. MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] Model is visually equivalent. Skipping rebuild...") + prepareMolecules(with: carouselModel) FormValidator.setupValidation(for: carouselModel, delegate: delegateObject?.formHolderDelegate) updateModelIndex() // Ensure the new model indexing matches the old. pagingView?.currentIndex = pageIndex // Trigger a paging view render. @@ -228,7 +231,7 @@ open class Carousel: View { //-------------------------------------------------- func prepareMolecules(with carouselModel: CarouselModel?) { - guard let newMolecules = carouselModel?.molecules else { + guard let newMolecules = carouselModel?.visibleMolecules else { numberOfPages = 0 molecules = nil return @@ -304,6 +307,7 @@ open class Carousel: View { pagingView?.removeFromSuperview() bottomPin?.isActive = false + pagingView = nil guard var pagingView = view else { bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) diff --git a/MVMCoreUI/Atomic/Organisms/Carousel/CarouselModel.swift b/MVMCoreUI/Atomic/Organisms/Carousel/CarouselModel.swift index 42c8363c..58ee28e6 100644 --- a/MVMCoreUI/Atomic/Organisms/Carousel/CarouselModel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel/CarouselModel.swift @@ -44,6 +44,10 @@ import UIKit public var selectable = false public var selectedIndex: Int? + public var visibleMolecules: [MoleculeModelProtocol & CarouselItemModelProtocol] { + molecules.filter { !$0.gone } + } + public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) { self.molecules = molecules } diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift index ed09d90e..24a0bb02 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift @@ -9,6 +9,7 @@ public protocol CarouselItemModelProtocol: FormFieldProtocol, ContainerModelProtocol { var analyticsData: JSONValueDictionary? { get set } + var gone: Bool { get set } } public extension CarouselItemModelProtocol { @@ -16,4 +17,9 @@ public extension CarouselItemModelProtocol { get { nil } set { analyticsData = newValue } } + + var gone: Bool{ + get { false } + set { } + } } diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/ParentMoleculeModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/ParentMoleculeModelProtocol.swift index d0e1c13f..13bb75b3 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/ParentMoleculeModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/ParentMoleculeModelProtocol.swift @@ -43,7 +43,7 @@ public extension ParentModelProtocol where Self: AnyObject { func replaceChildMolecule(in molecules: inout [T], with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? { var replacedMolecule: MoleculeModelProtocol? - return try replaceChildMolecule(at: &molecules, with: replacementMolecule, replaced: &replacedMolecule) ? replacedMolecule : nil + return try replaceChildMolecule(in: &molecules, with: replacementMolecule, replaced: &replacedMolecule) ? replacedMolecule : nil } /// Helper for replacing a molecule in place within an array. Note the "in". @@ -67,6 +67,26 @@ public protocol ParentMoleculeModelProtocol: ParentModelProtocol, MoleculeModelP public extension ParentMoleculeModelProtocol { + /// Recursively finds and replaces the first child matching the replacement molecule id property. + mutating func deepReplaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? { + var replacedMolecule: MoleculeModelProtocol? + var possibleError: Error? + // Dive into each root. + depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in + guard var parentMolecule = molecule as? ParentMoleculeModelProtocol else { return } + do { + replacedMolecule = try parentMolecule.replaceChildMolecule(with: replacementMolecule) + } catch { + possibleError = error + } + stop = replacedMolecule != nil || possibleError != nil + } + if let error = possibleError { + throw error + } + return replacedMolecule + } + func reduceDepthFirstTraverse(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int) -> Result) -> Result { var result = initialResult if (options == .parentFirst) { diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/TemplateModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/TemplateModelProtocol.swift index b598041b..b3d8bb59 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/TemplateModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/TemplateModelProtocol.swift @@ -40,31 +40,3 @@ public extension TemplateModelProtocol { return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit) } } - -extension TemplateModelProtocol { - - /// Recursively finds and replaces the first child matching the replacement molecule id property. - mutating func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? { - // Attempt root level replacement on the template model first. - if let replacedMolecule = try replaceChildMolecule(with: replacementMolecule) { - return replacedMolecule - } - - var replacedMolecule: MoleculeModelProtocol? - var possibleError: Error? - // Dive into each root thereafter. - depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in - guard var parentMolecule = molecule as? ParentMoleculeModelProtocol else { return } - do { - replacedMolecule = try parentMolecule.replaceChildMolecule(with: replacementMolecule) - } catch { - possibleError = error - } - stop = replacedMolecule != nil || possibleError != nil - } - if let error = possibleError { - throw error - } - return replacedMolecule - } -} diff --git a/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift b/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift index da57579d..ea4e0d88 100644 --- a/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift @@ -83,6 +83,9 @@ import Foundation let typeContainer = try decoder.container(keyedBy: CodingKeys.self) if let defaultPageType = Self.defaultPageType() { pageType = try typeContainer.decodeIfPresent(String.self, forKey: .pageType) ?? defaultPageType + if pageType.isEmpty { + pageType = defaultPageType + } } else { pageType = try typeContainer.decode(String.self, forKey: .pageType) } diff --git a/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift index e9e8b46f..a88e223b 100644 --- a/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift @@ -79,10 +79,10 @@ } - open override func handleNewData(_ pageModel: PageModelProtocol? = nil) { + open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) { setup() registerCells() - super.handleNewData(pageModel) + super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender) } open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) { diff --git a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift index 84608840..6678a5d1 100644 --- a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift @@ -25,8 +25,8 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate { try decoder.decode(ModalListPageTemplateModel.self, from: data) } - override open func handleNewData(_ pageModel: PageModelProtocol? = nil) { - super.handleNewData(pageModel) + override open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) { + super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender) closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in guard let self = self else { return } diff --git a/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift index d4753b2e..6cb62d69 100644 --- a/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift @@ -23,8 +23,8 @@ open class ModalMoleculeStackTemplate: MoleculeStackTemplate { // MARK: - Lifecycle //-------------------------------------------------- - override open func handleNewData(_ pageModel: PageModelProtocol? = nil) { - super.handleNewData(pageModel) + override open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) { + super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender) _ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in guard let self = self else { return } let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ?? diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 5eb23107..40bcd134 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -85,8 +85,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return view } - open override func handleNewData(_ pageModel: PageModelProtocol? = nil) { - super.handleNewData(pageModel) + open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) { + super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender) if pageModel != nil { setup() diff --git a/MVMCoreUI/Atomic/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeStackTemplate.swift index 8ce8cd67..bcfc2db4 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeStackTemplate.swift @@ -20,10 +20,10 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { // MARK: - Lifecycle //-------------------------------------------------- - open override func handleNewData(_ pageModel: PageModelProtocol? = nil) { + open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) { topViewOutsideOfScroll = templateModel?.anchorHeader ?? false bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false - super.handleNewData(pageModel) + super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender) } // For subclassing the model. diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index 9ae3b1c4..4bd889bd 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -52,17 +52,20 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController, Rotor bottomView.updateView(width) showFooter(width) } - tableView.reloadData() + tableView.visibleCells.forEach { cell in + (cell as? MVMCoreViewProtocol)?.updateView(width) + } } open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) { super.updateUI(for: molecules) guard molecules == nil else { return } - + createViewForTableHeader() createViewForTableFooter() - tableView?.reloadData() + // Reloading the table is handled in updateViews, however, update views is on a separate rendering task than the current thread. The table render needs to be bound and settled to the new model before others put in additional update requests. + tableView.reloadData() } override open func viewDidLoad() { diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index dc2ed356..78191da5 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -146,6 +146,11 @@ import MVMCore do { let template = try parsePageJSON(loadObject: loadObject) pageModel = template // TODO: Eventually this page parsing should be done outside of this class and then set by the caller. For now, double duty. + // Needed for PageMoleculeTransformationBehavior + PageLocalDataShareBehavior behaviors. + if let behaviorContainer = template as? (PageBehaviorContainerModelProtocol & TemplateModelProtocol) { + var behaviorHandler = self + behaviorHandler.applyBehaviors(pageBehaviorModel: behaviorContainer) + } isFirstRender = true // Assuming this is only on the first page load from the handler. Might need to revist later. if let backgroundRequest = loadObject.requestParameters?.backgroundRequest, !backgroundRequest, let pageType, let identifier = loadObject.identifier { MVMCoreLoggingHandler.shared()?.logCoreEvent(.pageProcessingComplete(pageType: pageType, requestUUID: identifier, webUrl: nil)) @@ -225,16 +230,16 @@ import MVMCore return navigationModel } - /// Processes any new data. Called after the page is loaded the first time and on response updates for this page. Triggers a render refresh. + /// Processes any new data. Called after the page is loaded the first time and on response updates for this page. Triggers a render refresh unless specified otherwise. @MainActor - open func handleNewData(_ pageModel: PageModelProtocol? = nil) { + open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) { guard var newPageModel = pageModel ?? self.pageModel else { return } - let originalModel = isFirstRender ? nil : self.pageModel as? MVMControllerModelProtocol + let originalModel = self.pageModel as? MVMControllerModelProtocol - // Refresh our behaviors if there is a page change. + // Refresh our behaviors if there is a page change. Originally set up in shouldFinishProcessingLoad. if let behaviorContainer = newPageModel as? (PageBehaviorContainerModelProtocol & TemplateModelProtocol), - (originalModel == nil || originalModel!.id != behaviorContainer.id) { + (originalModel == nil || originalModel!.id != behaviorContainer.id) { var behaviorHandler = self behaviorHandler.applyBehaviors(pageBehaviorModel: behaviorContainer) } @@ -277,15 +282,20 @@ import MVMCore let allUpdatedMolecules = behaviorUpdatedModels //+ pageUpdatedModels // Notify the manager of new data. - // Warning: Some flows cause table reloads. Until the UI update is decoupled, should be after the updateUI. manager?.newDataReceived?(in: self) - - if allUpdatedMolecules.isEmpty || isFirstRender { - debugLog("Performing full page render...") - updateUI() - } else { - debugLog("Performing partial render of \(allUpdatedMolecules) molecules...") - updateUI(for: allUpdatedMolecules) + + guard shouldTriggerRender else { return } + + // Dispatch to decouple execution. First massage data through template classes, then render. + Task { @MainActor in + + if allUpdatedMolecules.isEmpty || isFirstRender { + debugLog("Performing full page render...") + updateUI() + } else { + debugLog("Performing partial render of \(allUpdatedMolecules) molecules...") + updateUI(for: allUpdatedMolecules) + } } } @@ -399,7 +409,8 @@ import MVMCore initialLoad() } - handleNewData(pageModel) // Set outside shouldFinishProcessingLoad. + handleNewData(pageModel, shouldTriggerRender: false) // Set outside shouldFinishProcessingLoad. + updateUI() // Force the rendering on the same main UI thread. } open override func viewDidLayoutSubviews() { diff --git a/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift b/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift index 566a3f3d..ad50b9ed 100644 --- a/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift +++ b/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift @@ -41,7 +41,7 @@ public extension PageBehaviorHandlerProtocol { // Apply them to the page. self.behaviors = behaviors.count > 0 ? behaviors : nil - // Ask the session to apply any more. (Curently inverted contol due to Swift <--> Obj-C conflict. + // Ask the session to apply any more. (Currently inverted contol due to Swift <--> Obj-C conflict.) if let viewController = self as? UIViewController { MVMCoreUISession.sharedGlobal()?.applyGlobalBehaviors(to: viewController) } diff --git a/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift b/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift index c03594d4..c35a5d19 100644 --- a/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift +++ b/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift @@ -20,7 +20,7 @@ public class ReplaceableMoleculeBehaviorModel: PageBehaviorModelProtocol { } } -public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, CoreLogging { +public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, PageVisibilityBehavior, CoreLogging { public var loggingPrefix: String { "\(self) \(ObjectIdentifier(self).hashValue) \(moleculeIds.prefix(3)) \(moleculeIds.count > 3 ? "+ \(moleculeIds.count - 3) more" : ""):\n" @@ -30,6 +30,8 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co String(describing: Self.self) } + var isPageShowing = false + var previouslyReplacedIds = Set() var moleculeIds: [String] public var modulesToListenFor: [String] private var observingForResponses: NSObjectProtocol? @@ -41,7 +43,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co self.delegateObject = delegateObject guard let pageType = delegateObject?.moleculeDelegate?.getTemplateModel()?.pageType else { return } MVMCoreViewControllerMappingObject.shared()?.addOptionalModules(toMapping: moleculeIds, forPageType: pageType) - Self.debugLog("Initializing for \((model as! ReplaceableMoleculeBehaviorModel).moleculeIds)") + Self.debugLog("Initializing for \(moleculeIds)") } public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?, changes: inout [MoleculeModelProtocol]) -> [MoleculeModelProtocol]? { @@ -75,7 +77,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co return rootMolecule } debugLog("top replacing \(rootMolecule) with \(updatedMolecule)") - logUpdated(molecule: updatedMolecule) + logUpdated(moleculeId: updatedMolecule.id) changeList.append(updatedMolecule) return updatedMolecule } @@ -85,14 +87,14 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co moleculeModels.forEach { newMolecule in do { - if let replacedMolecule = try parentMolecule.replaceChildMolecule(with: newMolecule) { + if let replacedMolecule = try parentMolecule.deepReplaceMolecule(with: newMolecule) { guard !replacedMolecule.deepEquals(to: newMolecule) else { // Note: Slight risk here of replacing the something in the original tree and misreporting that is it not replaced based on equality. debugLog("deep molecule \(newMolecule) is the same as \(replacedMolecule). skipping...") return } debugLog("deep replacing \(replacedMolecule) with \(newMolecule)") - logUpdated(molecule: newMolecule) + logUpdated(moleculeId: newMolecule.id) changeList.append(newMolecule) } } catch { @@ -111,15 +113,29 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co return hasReplacement ? updatedRootMolecules : nil } - private func logUpdated(molecule: MoleculeModelProtocol) { - guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(molecule.id), + private func logUpdated(moleculeId: String) { + guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(moleculeId), let viewController = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { - debugLog("Missing the originating module \(molecule.id) creating this molecule!") + debugLog("Missing the originating module \(moleculeId) creating this molecule!") return } + previouslyReplacedIds.insert(moleculeId) + guard isPageShowing else { return } // Page has not been made visible yet. (Pulled and replaced from cache on load or background update.) Hold reporting until onPageShown. MVMCoreUILoggingHandler.shared()?.defaultLogPageUpdate(forController: viewController, from: module) } + public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) { + isPageShowing = true + debugLog("Page shown. Send molecule analytics for: \(previouslyReplacedIds)") + previouslyReplacedIds.forEach { id in + logUpdated(moleculeId: id) + } + } + + public func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?) { + isPageShowing = false + } + deinit { debugLog("deinit") } diff --git a/MVMCoreUI/Categories/UIColor+Extension.swift b/MVMCoreUI/Categories/UIColor+Extension.swift index 1d2158a6..59be616b 100644 --- a/MVMCoreUI/Categories/UIColor+Extension.swift +++ b/MVMCoreUI/Categories/UIColor+Extension.swift @@ -7,6 +7,7 @@ // import UIKit +import VDSCoreTokens public typealias ColorHexTuple = (uiColor: UIColor, hex: String) @@ -19,7 +20,8 @@ extension UIColor { /// Dictionary to access brand approved colors by name. public static let names: [String: ColorHexTuple] = ["black": (.mvmBlack, "#000000"), "white": (.mvmWhite, "#FFFFFF"), - "red": (.mvmRed, "#D52B1E"), + "red": (.mvmRed, "#EE0000"), + "monarchRed": (VDSCoreTokens.VDSColor.paletteMonarchred, "#f50a23"), "pink": (.mvmPink, "#D90368"), "pink33": (.mvmPink33, "#F2ABCD"), "pink66": (.mvmPink66, "#E6589B"), @@ -48,13 +50,20 @@ extension UIColor { "blueShade2": (.mvmBlueShade2, "#0B4467"), "blueInverted": (.mvmBlueInverted, "#0088CE"), "yellow": (.mvmYellow, "#FFBC3D"), + "neonYellow": (VDSCoreTokens.VDSColor.paletteNeonyellow, "#f5ff1e"), "coolGray1": (.mvmCoolGray1, "#F6F6F6"), "coolGray3": (.mvmCoolGray3, "#D8DADA"), "coolGray6": (.mvmCoolGray6, "#747676"), "coolGray10": (.mvmCoolGray10, "#333333"), "upGold1": (.vzupGold1, "#F9D542"), "upGold2": (.vzupGold2, "#F4CA53"), - "upGold3": (.vzupGold3, "#CC9B2D")] + "upGold3": (.vzupGold3, "#CC9B2D"), + "stone": (.stone, "#F3EDE0"), + "coral": (.coral, "#FF3C2D"), + "gray44": (.gray44, "#6F7171"), + "gray85": (.gray85, "#D8DADA"), + "gray95": (.gray95, "#F6F6F6") + ] //-------------------------------------------------- // MARK: - Helper @@ -82,9 +91,10 @@ extension UIColor { // MARK: - Red //-------------------------------------------------- - /// HEX: #D52B1E + /// HEX: #EE0000 + @objc public static let mvmRed = UIColor.assetColor(named: "red") - + //-------------------------------------------------- // MARK: - Pink //-------------------------------------------------- @@ -190,7 +200,7 @@ extension UIColor { // MARK: - Yellow //-------------------------------------------------- - /// HEX: #FFBC3D + /// HEX: ##F5FF1E public static let mvmYellow = UIColor.assetColor(named: "yellow") //-------------------------------------------------- @@ -222,6 +232,25 @@ extension UIColor { /// HEX: #CC9B2D public static let vzupGold3 = UIColor.assetColor(named: "upGold3") + //-------------------------------------------------- + // MARK: - Monarch + //-------------------------------------------------- + + /// HEX: #F3EDE0 + @objc public static let stone = VDSCoreTokens.VDSColor.paletteStone + + // HEX:#FF3C2D + @objc public static let coral = VDSCoreTokens.VDSColor.paletteCoral + + // HEX:#6F7171 + @objc public static let gray44 = VDSCoreTokens.VDSColor.paletteGray44 + + // HEX:#D8DADA + @objc public static let gray85 = VDSCoreTokens.VDSColor.paletteGray85 + + // HEX:#F6F6F6 + @objc public static let gray95 = VDSCoreTokens.VDSColor.paletteGray95 + //-------------------------------------------------- // MARK: - Functions //-------------------------------------------------- diff --git a/MVMCoreUI/Categories/UIColor+MFConvenience.m b/MVMCoreUI/Categories/UIColor+MFConvenience.m index f33b71b0..d10672a8 100644 --- a/MVMCoreUI/Categories/UIColor+MFConvenience.m +++ b/MVMCoreUI/Categories/UIColor+MFConvenience.m @@ -77,7 +77,7 @@ #pragma mark - legacy + (nonnull UIColor *)mfRedColor { - return [UIColor colorWithRed:.804 green:.016 blue:.043 alpha:1.0]; + return [UIColor mvmRed]; } + (nonnull UIColor *)mfDarkerRedColor { @@ -307,8 +307,6 @@ static dispatch_once_t once; dispatch_once(&once, ^{ stringColorMapping = @{@"PrimaryRed":[UIColor mfRedColor], - @"black":[UIColor blackColor], - @"red":[UIColor mfRedColor], @"greyish":[UIColor mfLightGrayColor], @"robinsEggBlue" : [UIColor mfRobinsEggBlue], @"lightSalmon" : [UIColor mfLightSalmon], diff --git a/MVMCoreUI/Categories/colors.xcassets/red.colorset/Contents.json b/MVMCoreUI/Categories/colors.xcassets/red.colorset/Contents.json index 536ccc9b..3a204689 100644 --- a/MVMCoreUI/Categories/colors.xcassets/red.colorset/Contents.json +++ b/MVMCoreUI/Categories/colors.xcassets/red.colorset/Contents.json @@ -1,20 +1,20 @@ { - "info" : { - "version" : 1, - "author" : "xcode" - }, "colors" : [ { - "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { - "red" : "0xD5", "alpha" : "1.000", - "blue" : "0x1E", - "green" : "0x2B" + "blue" : "0x00", + "green" : "0x00", + "red" : "0xEE" } - } + }, + "idiom" : "universal" } - ] -} \ No newline at end of file + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MVMCoreUI/Categories/colors.xcassets/yellow.colorset/Contents.json b/MVMCoreUI/Categories/colors.xcassets/yellow.colorset/Contents.json index 5c00b5eb..8467664a 100644 --- a/MVMCoreUI/Categories/colors.xcassets/yellow.colorset/Contents.json +++ b/MVMCoreUI/Categories/colors.xcassets/yellow.colorset/Contents.json @@ -1,20 +1,20 @@ { - "info" : { - "version" : 1, - "author" : "xcode" - }, "colors" : [ { - "idiom" : "universal", "color" : { "color-space" : "srgb", "components" : { - "red" : "0xFF", "alpha" : "1.000", "blue" : "0x3D", - "green" : "0xBC" + "green" : "0xBC", + "red" : "0xFF" } - } + }, + "idiom" : "universal" } - ] -} \ No newline at end of file + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift b/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift index 8a768545..d5e0182e 100644 --- a/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift +++ b/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift @@ -8,7 +8,7 @@ import Foundation import MVMCore -import VDSTokens +import VDSCoreTokens open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, TabsDelegate, MVMCorePresentationDelegateProtocol, SubNavSwipeNavigationProtocol { /// The number of tabs count or less that will turn on the fillContainer diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Contents.json index 07a5a9f8..702db438 100644 --- a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Contents.json +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Contents.json @@ -1,23 +1,15 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "exportBlack.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "exportBlack@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "exportBlack@3x.png", - "scale" : "3x" + "filename" : "Vector.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } -} \ No newline at end of file +} diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Vector.svg b/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Vector.svg new file mode 100644 index 00000000..bcdaef29 --- /dev/null +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack.png b/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack.png deleted file mode 100644 index 34f6d7e9..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack@2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack@2x.png deleted file mode 100644 index c6de73e8..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack@2x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack@3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack@3x.png deleted file mode 100644 index 15f13e32..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/exportBlack@3x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Contents.json index 56ae55a3..702db438 100644 --- a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Contents.json +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Contents.json @@ -1,23 +1,15 @@ { "images" : [ { - "filename" : "nav_back.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "nav_back@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "nav_back@3x.png", - "idiom" : "universal", - "scale" : "3x" + "filename" : "Vector.svg", + "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Vector.svg b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Vector.svg new file mode 100644 index 00000000..a0d9c7b8 --- /dev/null +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back.png deleted file mode 100644 index 895fade7..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@2x.png deleted file mode 100644 index e699cb47..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@2x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@3x.png deleted file mode 100644 index 96e839ed..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@3x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close-1.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close-1.png deleted file mode 100644 index dd579598..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close-1.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close-2.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close-2.png deleted file mode 100644 index 3cbe291b..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close-2.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close.png deleted file mode 100644 index dac97806..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Close.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Contents.json index ffda34e6..702db438 100644 --- a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Contents.json +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Contents.json @@ -1,23 +1,15 @@ { "images" : [ { - "filename" : "Close.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "Close-1.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "Close-2.png", - "idiom" : "universal", - "scale" : "3x" + "filename" : "Vector.svg", + "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Vector.svg b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Vector.svg new file mode 100644 index 00000000..1aece044 --- /dev/null +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.h b/MVMCoreUI/Utility/MVMCoreUIUtility.h index c6959723..12c1aa34 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.h +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.h @@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN // The bundle for this framework + (nullable NSBundle *)bundleForMVMCoreUI; +/// The bundle for the VDS frameowrk. Handy for accessing VDS resources such as fonts. ++ (nullable NSBundle *)bundleForFonts; + // Returns the hardcoded string from the string file. + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key; diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index ad7366df..e725114d 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -22,6 +22,10 @@ return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCoreUI"]; } ++ (nullable NSBundle *)bundleForFonts { + return [NSBundle bundleWithIdentifier:@"com.vzw.vds"]; +} + + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key { // Redirect key with relevant module. return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreUIUtility bundleForMVMCoreUI]];